172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
28ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Use of this source code is governed by a BSD-style license that can be
38ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// found in the LICENSE file.
48ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
58ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension_file_util.h"
68ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
78ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include <map>
88ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include <vector>
98ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/file_util.h"
118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/logging.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h"
1372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/metrics/histogram.h"
1472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/path_service.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/threading/thread_restrictions.h"
168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/utf_string_conversions.h"
1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/common/chrome_paths.h"
188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension.h"
198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension_action.h"
208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension_l10n_util.h"
218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension_constants.h"
228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension_resource.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/common/extensions/extension_sidebar_defaults.h"
24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/json_value_serializer.h"
258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "grit/generated_resources.h"
268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "net/base/escape.h"
278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "net/base/file_stream.h"
2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsennamespace errors = extension_manifest_errors;
318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsennamespace extension_file_util {
338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Validates locale info. Doesn't check if messages.json files are valid.
358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic bool ValidateLocaleInfo(const Extension& extension, std::string* error);
368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Returns false and sets the error if script file can't be loaded,
388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// or if it's not UTF-8 encoded.
398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic bool IsScriptValid(const FilePath& path, const FilePath& relative_path,
408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          int message_id, std::string* error);
418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst char kInstallDirectoryName[] = "Extensions";
438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
448ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenFilePath InstallExtension(const FilePath& unpacked_source_dir,
458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          const std::string& id,
468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          const std::string& version,
478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          const FilePath& all_extensions_dir) {
488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath extension_dir = all_extensions_dir.AppendASCII(id);
498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath version_dir;
508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Create the extension directory if it doesn't exist already.
528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!file_util::PathExists(extension_dir)) {
538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!file_util::CreateDirectory(extension_dir))
548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return FilePath();
558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Try to find a free directory. There can be legitimate conflicts in the case
588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // of overinstallation of the same version.
598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  const int kMaxAttempts = 100;
608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (int i = 0; i < kMaxAttempts; ++i) {
618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath candidate = extension_dir.AppendASCII(
62513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch        base::StringPrintf("%s_%u", version.c_str(), i));
638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!file_util::PathExists(candidate)) {
648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      version_dir = candidate;
658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      break;
668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (version_dir.empty()) {
708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    LOG(ERROR) << "Could not find a home for extension " << id << " with "
718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen               << "version " << version << ".";
728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return FilePath();
738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!file_util::Move(unpacked_source_dir, version_dir))
768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return FilePath();
778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return version_dir;
798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid UninstallExtension(const FilePath& extensions_dir,
828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                        const std::string& id) {
838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // We don't care about the return value. If this fails (and it can, due to
848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // plugins that aren't unloaded yet, it will get cleaned up by
8521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // ExtensionService::GarbageCollectExtensions).
868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  file_util::Delete(extensions_dir.AppendASCII(id), true);  // recursive.
878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
89513209b27ff55e2841eac0e4120199c23acce758Ben Murdochscoped_refptr<Extension> LoadExtension(const FilePath& extension_path,
90513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                       Extension::Location location,
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                       int flags,
92513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                       std::string* error) {
938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath manifest_path =
948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      extension_path.Append(Extension::kManifestFilename);
958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!file_util::PathExists(manifest_path)) {
96513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE);
978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  JSONFileValueSerializer serializer(manifest_path);
1018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  scoped_ptr<Value> root(serializer.Deserialize(NULL, error));
1028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!root.get()) {
1038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (error->empty()) {
1048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      // If |error| is empty, than the file could not be read.
1058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      // It would be cleaner to have the JSON reader give a specific error
1068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      // in this case, but other code tests for a file error with
1078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      // error->empty().  For now, be consistent.
108513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE);
1098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    } else {
110513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      *error = base::StringPrintf("%s  %s",
111513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                  errors::kManifestParseError,
112513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                  error->c_str());
1138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
1148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
1158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!root->IsType(Value::TYPE_DICTIONARY)) {
118513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID);
1198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
1208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  DictionaryValue* manifest = static_cast<DictionaryValue*>(root.get());
123513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (!extension_l10n_util::LocalizeExtension(extension_path, manifest, error))
1248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
1258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
126513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  scoped_refptr<Extension> extension(Extension::Create(
127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      extension_path,
128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      location,
129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      *manifest,
130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      flags,
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      error));
132513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (!extension.get())
1338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
1348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!ValidateExtension(extension.get(), error))
1368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
1378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
138513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  return extension;
1398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenbool ValidateExtension(Extension* extension, std::string* error) {
1428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate icons exist.
1438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (ExtensionIconSet::IconMap::const_iterator iter =
1448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen           extension->icons().map().begin();
1458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen       iter != extension->icons().map().end();
1468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen       ++iter) {
1478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const FilePath path = extension->GetResource(iter->second).GetFilePath();
1488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!file_util::PathExists(path)) {
1498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error =
1508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_ICON_FAILED,
1518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                    UTF8ToUTF16(iter->second));
1528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
1538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
1548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Theme resource validation.
1578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (extension->is_theme()) {
1588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    DictionaryValue* images_value = extension->GetThemeImages();
1598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (images_value) {
1608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      for (DictionaryValue::key_iterator iter = images_value->begin_keys();
1618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen           iter != images_value->end_keys(); ++iter) {
1628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        std::string val;
1638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        if (images_value->GetStringWithoutPathExpansion(*iter, &val)) {
1648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          FilePath image_path = extension->path().AppendASCII(val);
1658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          if (!file_util::PathExists(image_path)) {
1668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen            *error =
1678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                l10n_util::GetStringFUTF8(IDS_EXTENSION_INVALID_IMAGE_PATH,
16872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                          image_path.LossyDisplayName());
1698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen            return false;
1708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          }
1718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        }
1728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      }
1738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
1748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // Themes cannot contain other extension types.
1768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return true;
1778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate that claimed script resources actually exist,
1808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // and are UTF-8 encoded.
1818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
1828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const UserScript& script = extension->content_scripts()[i];
1838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    for (size_t j = 0; j < script.js_scripts().size(); j++) {
1858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      const UserScript::File& js_script = script.js_scripts()[j];
1868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      const FilePath& path = ExtensionResource::GetFilePath(
1878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          js_script.extension_root(), js_script.relative_path());
1888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      if (!IsScriptValid(path, js_script.relative_path(),
1898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                         IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error))
1908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        return false;
1918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
1928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    for (size_t j = 0; j < script.css_scripts().size(); j++) {
1948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      const UserScript::File& css_script = script.css_scripts()[j];
1958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      const FilePath& path = ExtensionResource::GetFilePath(
1968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          css_script.extension_root(), css_script.relative_path());
1978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      if (!IsScriptValid(path, css_script.relative_path(),
1988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                         IDS_EXTENSION_LOAD_CSS_FAILED, error))
1998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        return false;
2008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate claimed plugin paths.
2048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (size_t i = 0; i < extension->plugins().size(); ++i) {
2058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const Extension::PluginInfo& plugin = extension->plugins()[i];
2068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!file_util::PathExists(plugin.path)) {
2078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error =
2088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          l10n_util::GetStringFUTF8(
2098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen              IDS_EXTENSION_LOAD_PLUGIN_PATH_FAILED,
21072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              plugin.path.LossyDisplayName());
2118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
2128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate icon location for page actions.
2168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  ExtensionAction* page_action = extension->page_action();
2178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (page_action) {
2188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    std::vector<std::string> icon_paths(*page_action->icon_paths());
2198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!page_action->default_icon_path().empty())
2208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      icon_paths.push_back(page_action->default_icon_path());
2218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    for (std::vector<std::string>::iterator iter = icon_paths.begin();
2228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen         iter != icon_paths.end(); ++iter) {
2238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      if (!file_util::PathExists(extension->GetResource(*iter).GetFilePath())) {
2248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        *error =
2258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen            l10n_util::GetStringFUTF8(
2268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                IDS_EXTENSION_LOAD_ICON_FOR_PAGE_ACTION_FAILED,
2278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                UTF8ToUTF16(*iter));
2288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        return false;
2298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      }
2308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate icon location for browser actions.
2348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Note: browser actions don't use the icon_paths().
2358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  ExtensionAction* browser_action = extension->browser_action();
2368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (browser_action) {
2378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    std::string path = browser_action->default_icon_path();
2388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!path.empty() &&
2398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        !file_util::PathExists(extension->GetResource(path).GetFilePath())) {
2408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        *error =
2418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen            l10n_util::GetStringFUTF8(
2428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                IDS_EXTENSION_LOAD_ICON_FOR_BROWSER_ACTION_FAILED,
2438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                UTF8ToUTF16(path));
2448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        return false;
2458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Validate background page location, except for hosted apps, which should use
249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // an external URL. Background page for hosted apps are verified when the
250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // extension is created (in Extension::InitFromValue)
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!extension->background_url().is_empty() && !extension->is_hosted_app()) {
2528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath page_path = ExtensionURLToRelativeFilePath(
2538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        extension->background_url());
2548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const FilePath path = extension->GetResource(page_path).GetFilePath();
2558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (path.empty() || !file_util::PathExists(path)) {
2568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error =
2578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          l10n_util::GetStringFUTF8(
2588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen              IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED,
25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              page_path.LossyDisplayName());
2608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
2618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate path to the options page.  Don't check the URL for hosted apps,
2658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // because they are expected to refer to an external URL.
2668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!extension->options_url().is_empty() && !extension->is_hosted_app()) {
2678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const FilePath options_path = ExtensionURLToRelativeFilePath(
2688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        extension->options_url());
2698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const FilePath path = extension->GetResource(options_path).GetFilePath();
2708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (path.empty() || !file_util::PathExists(path)) {
2718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error =
2728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          l10n_util::GetStringFUTF8(
2738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen              IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED,
27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              options_path.LossyDisplayName());
27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return false;
27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Validate sidebar default page location.
28072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ExtensionSidebarDefaults* sidebar_defaults = extension->sidebar_defaults();
28172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (sidebar_defaults && sidebar_defaults->default_page().is_valid()) {
28272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    FilePath page_path = ExtensionURLToRelativeFilePath(
28372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        sidebar_defaults->default_page());
28472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const FilePath path = extension->GetResource(page_path).GetFilePath();
28572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (path.empty() || !file_util::PathExists(path)) {
28672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      *error =
28772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          l10n_util::GetStringFUTF8(
28872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              IDS_EXTENSION_LOAD_SIDEBAR_PAGE_FAILED,
28972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              page_path.LossyDisplayName());
2908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
2918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Validate locale info.
2958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!ValidateLocaleInfo(*extension, error))
2968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Check children of extension root to see if any of them start with _ and is
2998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // not on the reserved list.
3008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!CheckForIllegalFilenames(extension->path(), error)) {
3018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
3028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
3038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return true;
3058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid GarbageCollectExtensions(
3088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const FilePath& install_directory,
3098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const std::map<std::string, FilePath>& extension_paths) {
3108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Nothing to clean up if it doesn't exist.
3118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!file_util::DirectoryExists(install_directory))
3128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return;
3138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
314731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  VLOG(1) << "Garbage collecting extensions...";
3158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  file_util::FileEnumerator enumerator(install_directory,
3168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                       false,  // Not recursive.
3178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                       file_util::FileEnumerator::DIRECTORIES);
3188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath extension_path;
3198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (extension_path = enumerator.Next(); !extension_path.value().empty();
3208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen       extension_path = enumerator.Next()) {
32172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    std::string extension_id;
32272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    FilePath basename = extension_path.BaseName();
32472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (IsStringASCII(basename.value())) {
32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      extension_id = UTF16ToASCII(basename.LossyDisplayName());
32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!Extension::IdIsValid(extension_id))
32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        extension_id.clear();
32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
3298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // Delete directories that aren't valid IDs.
33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (extension_id.empty()) {
3328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      LOG(WARNING) << "Invalid extension ID encountered in extensions "
33372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      "directory: " << basename.value();
334731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      VLOG(1) << "Deleting invalid extension directory "
33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              << extension_path.value() << ".";
3368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      file_util::Delete(extension_path, true);  // Recursive.
3378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      continue;
3388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
3398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    std::map<std::string, FilePath>::const_iterator iter =
3418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        extension_paths.find(extension_id);
3428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // If there is no entry in the prefs file, just delete the directory and
3448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // move on. This can legitimately happen when an uninstall does not
3458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // complete, for example, when a plugin is in use at uninstall time.
3468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (iter == extension_paths.end()) {
347731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      VLOG(1) << "Deleting unreferenced install for directory "
34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              << extension_path.LossyDisplayName() << ".";
3498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      file_util::Delete(extension_path, true);  // Recursive.
3508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      continue;
3518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
3528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // Clean up old version directories.
3548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    file_util::FileEnumerator versions_enumerator(
3558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        extension_path,
3568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        false,  // Not recursive.
3578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        file_util::FileEnumerator::DIRECTORIES);
3588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    for (FilePath version_dir = versions_enumerator.Next();
3598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen         !version_dir.value().empty();
3608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen         version_dir = versions_enumerator.Next()) {
3618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      if (version_dir.BaseName() != iter->second.BaseName()) {
362731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        VLOG(1) << "Deleting old version for directory "
36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                << version_dir.LossyDisplayName() << ".";
3648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        file_util::Delete(version_dir, true);  // Recursive.
3658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      }
3668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
3678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
3688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3708ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenExtensionMessageBundle* LoadExtensionMessageBundle(
3718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const FilePath& extension_path,
3728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    const std::string& default_locale,
3738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    std::string* error) {
3748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  error->clear();
3758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Load locale information if available.
3768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath locale_path = extension_path.Append(Extension::kLocaleFolder);
3778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!file_util::PathExists(locale_path))
3788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
3798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::set<std::string> locales;
3818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!extension_l10n_util::GetValidLocales(locale_path, &locales, error))
3828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
3838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (default_locale.empty() ||
3858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      locales.find(default_locale) == locales.end()) {
3868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error = l10n_util::GetStringUTF8(
3878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED);
3888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return NULL;
3898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
3908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  ExtensionMessageBundle* message_bundle =
3928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      extension_l10n_util::LoadMessageCatalogs(
3938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          locale_path,
3948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          default_locale,
3958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          extension_l10n_util::CurrentLocaleOrDefault(),
3968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          locales,
3978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          error);
3988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return message_bundle;
4008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic bool ValidateLocaleInfo(const Extension& extension, std::string* error) {
4038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // default_locale and _locales have to be both present or both missing.
4048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  const FilePath path = extension.path().Append(Extension::kLocaleFolder);
4058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  bool path_exists = file_util::PathExists(path);
4068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::string default_locale = extension.default_locale();
4078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // If both default locale and _locales folder are empty, skip verification.
4098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (default_locale.empty() && !path_exists)
4108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return true;
4118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (default_locale.empty() && path_exists) {
4138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error = l10n_util::GetStringUTF8(
4148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED);
4158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
4168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  } else if (!default_locale.empty() && !path_exists) {
4178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error = errors::kLocalesTreeMissing;
4188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
4198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
4208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Treat all folders under _locales as valid locales.
4228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  file_util::FileEnumerator locales(path,
4238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                    false,
4248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                    file_util::FileEnumerator::DIRECTORIES);
4258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::set<std::string> all_locales;
4278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  extension_l10n_util::GetAllLocales(&all_locales);
4288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  const FilePath default_locale_path = path.AppendASCII(default_locale);
4298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  bool has_default_locale_message_file = false;
4308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath locale_path;
4328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  while (!(locale_path = locales.Next()).empty()) {
4338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (extension_l10n_util::ShouldSkipValidation(path, locale_path,
4348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                                  all_locales))
4358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      continue;
4368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath messages_path =
4388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        locale_path.Append(Extension::kMessagesFilename);
4398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!file_util::PathExists(messages_path)) {
441513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      *error = base::StringPrintf(
4428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          "%s %s", errors::kLocalesMessagesFileMissing,
44372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          UTF16ToUTF8(messages_path.LossyDisplayName()).c_str());
4448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
4458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
4468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (locale_path == default_locale_path)
4488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      has_default_locale_message_file = true;
4498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
4508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Only message file for default locale has to exist.
4528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!has_default_locale_message_file) {
4538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error = errors::kLocalesNoDefaultMessages;
4548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
4558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
4568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return true;
4588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic bool IsScriptValid(const FilePath& path,
4618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          const FilePath& relative_path,
4628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          int message_id,
4638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          std::string* error) {
4648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::string content;
4658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!file_util::PathExists(path) ||
4668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      !file_util::ReadFileToString(path, &content)) {
4678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error = l10n_util::GetStringFUTF8(
4688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        message_id,
46972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        relative_path.LossyDisplayName());
4708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
4718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
4728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!IsStringUTF8(content)) {
4748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error = l10n_util::GetStringFUTF8(
4758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        IDS_EXTENSION_BAD_FILE_ENCODING,
47672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        relative_path.LossyDisplayName());
4778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
4788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
4798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return true;
4818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenbool CheckForIllegalFilenames(const FilePath& extension_path,
4848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                              std::string* error) {
4858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Reserved underscore names.
4868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  static const FilePath::CharType* reserved_names[] = {
4878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    Extension::kLocaleFolder,
4888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FILE_PATH_LITERAL("__MACOSX"),
4898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  };
4908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  static std::set<FilePath::StringType> reserved_underscore_names(
4918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      reserved_names, reserved_names + arraysize(reserved_names));
4928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Enumerate all files and directories in the extension root.
4948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // There is a problem when using pattern "_*" with FileEnumerator, so we have
4958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // to cheat with find_first_of and match all.
4968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  file_util::FileEnumerator all_files(
4978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    extension_path,
4988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    false,
4998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    static_cast<file_util::FileEnumerator::FILE_TYPE>(
5008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        file_util::FileEnumerator::DIRECTORIES |
5018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          file_util::FileEnumerator::FILES));
5028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath file;
5048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  while (!(file = all_files.Next()).empty()) {
5058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath::StringType filename = file.BaseName().value();
5068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // Skip all that don't start with "_".
5078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (filename.find_first_of(FILE_PATH_LITERAL("_")) != 0) continue;
5088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (reserved_underscore_names.find(filename) ==
5098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        reserved_underscore_names.end()) {
510513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      *error = base::StringPrintf(
511513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          "Cannot load extension with file or directory name %s. "
512513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          "Filenames starting with \"_\" are reserved for use by the system.",
513513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          filename.c_str());
5148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
5158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
5168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
5178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return true;
5198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
5208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5218ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenFilePath ExtensionURLToRelativeFilePath(const GURL& url) {
5228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::string url_path = url.path();
5238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (url_path.empty() || url_path[0] != '/')
5248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return FilePath();
5258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Drop the leading slashes and convert %-encoded UTF8 to regular UTF8.
5278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::string file_path = UnescapeURLComponent(url_path,
5288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
5298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  size_t skip = file_path.find_first_not_of("/\\");
5308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (skip != file_path.npos)
5318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    file_path = file_path.substr(skip);
5328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  FilePath path =
5348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#if defined(OS_POSIX)
5358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath(file_path);
5368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#elif defined(OS_WIN)
5378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath(UTF8ToWide(file_path));
5388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#else
5398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    FilePath();
5408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    NOTIMPLEMENTED();
5418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#endif
5428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // It's still possible for someone to construct an annoying URL whose path
5448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // would still wind up not being considered relative at this point.
5458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // For example: chrome-extension://id/c:////foo.html
5468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (path.IsAbsolute())
5478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return FilePath();
5488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
5498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return path;
5508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
5518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
55272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenFilePath GetUserDataTempDir() {
55372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // We do file IO in this function, but only when the current profile's
55472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Temp directory has never been used before, or in a rare error case.
55572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Developers are not likely to see these situations often, so do an
55672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // explicit thread check.
55772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
55872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
55972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Getting chrome::DIR_USER_DATA_TEMP is failing.  Use histogram to see why.
56072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(skerner): Fix the problem, and remove this code.  crbug.com/70056
56172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  enum DirectoryCreationResult {
56272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    SUCCESS = 0,
56372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
56472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CANT_GET_PARENT_PATH,
56572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CANT_GET_UDT_PATH,
56672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    NOT_A_DIRECTORY,
56772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CANT_CREATE_DIR,
56872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CANT_WRITE_TO_PATH,
56972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
57072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    UNSET,
57172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    NUM_DIRECTORY_CREATION_RESULTS
57272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  };
57372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
57472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // All paths should set |result|.
57572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DirectoryCreationResult result = UNSET;
57672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
57772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  FilePath temp_path;
57872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!PathService::Get(chrome::DIR_USER_DATA_TEMP, &temp_path)) {
57972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    FilePath parent_path;
58072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!PathService::Get(chrome::DIR_USER_DATA, &parent_path))
58172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = CANT_GET_PARENT_PATH;
58272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    else
58372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = CANT_GET_UDT_PATH;
58472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
58572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else if (file_util::PathExists(temp_path)) {
58672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
58772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Path exists.  Check that it is a directory we can write to.
58872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!file_util::DirectoryExists(temp_path)) {
58972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = NOT_A_DIRECTORY;
59072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
59172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else if (!file_util::PathIsWritable(temp_path)) {
59272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = CANT_WRITE_TO_PATH;
59372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
59472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
59572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // Temp is a writable directory.
59672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = SUCCESS;
59772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
59872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
59972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else if (!file_util::CreateDirectory(temp_path)) {
60072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Path doesn't exist, and we failed to create it.
60172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    result = CANT_CREATE_DIR;
60272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
60372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else {
60472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Successfully created the Temp directory.
60572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    result = SUCCESS;
60672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
60772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
60872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  UMA_HISTOGRAM_ENUMERATION("Extensions.GetUserDataTempDir",
60972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                            result,
61072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                            NUM_DIRECTORY_CREATION_RESULTS);
61172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
61272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result == SUCCESS)
61372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return temp_path;
61472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
61572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return FilePath();
61672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
61772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
618dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid DeleteFile(const FilePath& path, bool recursive) {
619dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  file_util::Delete(path, recursive);
620dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
621dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
6228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}  // namespace extension_file_util
623