172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/convert_user_script.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/base64.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_path.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/sha2.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/user_script_master.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_constants.h"
2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/common/extensions/extension_file_util.h"
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/user_script.h"
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/json_value_serializer.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/gurl.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace keys = extension_manifest_keys;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
28513209b27ff55e2841eac0e4120199c23acce758Ben Murdochscoped_refptr<Extension> ConvertUserScriptToExtension(
29513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    const FilePath& user_script_path, const GURL& original_url,
30513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    std::string* error) {
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string content;
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!file_util::ReadFileToString(user_script_path, &content)) {
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *error = "Could not read source file.";
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (!IsStringUTF8(content)) {
3821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    *error = "User script must be UTF8 encoded.";
3921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return NULL;
4021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
4121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UserScript script;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!UserScriptMaster::ScriptReloader::ParseMetadataHeader(content,
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                             &script)) {
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *error = "Invalid script header.";
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  FilePath user_data_temp_dir = extension_file_util::GetUserDataTempDir();
5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (user_data_temp_dir.empty()) {
5172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *error = "Could not get path to profile temporary directory.";
5272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return NULL;
5372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ScopedTempDir temp_dir;
563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!temp_dir.CreateUniqueTempDirUnderPath(user_data_temp_dir)) {
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *error = "Could not create temporary directory.";
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the manifest
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<DictionaryValue> root(new DictionaryValue);
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string script_name;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!script.name().empty() && !script.name_space().empty())
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    script_name = script.name_space() + "/" + script.name();
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    script_name = original_url.spec();
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the public key.
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // User scripts are not signed, but the public key for an extension doubles as
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // its unique identity, and we need one of those. A user script's unique
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // identity is its namespace+name, so we hash that to create a public key.
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // There will be no corresponding private key, which means user scripts cannot
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // be auto-updated, or claimed in the gallery.
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  char raw[crypto::SHA256_LENGTH] = {0};
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string key;
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  crypto::SHA256HashString(script_name, raw, crypto::SHA256_LENGTH);
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::Base64Encode(std::string(raw, crypto::SHA256_LENGTH), &key);
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The script may not have a name field, but we need one for an extension. If
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // it is missing, use the filename of the original URL.
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!script.name().empty())
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    root->SetString(keys::kName, script.name());
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    root->SetString(keys::kName, original_url.ExtractFileName());
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Not all scripts have a version, but we need one. Default to 1.0 if it is
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // missing.
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!script.version().empty())
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    root->SetString(keys::kVersion, script.version());
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    root->SetString(keys::kVersion, "1.0");
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  root->SetString(keys::kDescription, script.description());
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  root->SetString(keys::kPublicKey, key);
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  root->SetBoolean(keys::kConvertedFromUserScript, true);
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* js_files = new ListValue();
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  js_files->Append(Value::CreateStringValue("script.js"));
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the script provides its own match patterns, we use those. Otherwise, we
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // generate some using the include globs.
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* matches = new ListValue();
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!script.url_patterns().empty()) {
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (size_t i = 0; i < script.url_patterns().size(); ++i) {
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      matches->Append(Value::CreateStringValue(
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          script.url_patterns()[i].GetAsString()));
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(aa): Derive tighter matches where possible.
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    matches->Append(Value::CreateStringValue("http://*/*"));
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    matches->Append(Value::CreateStringValue("https://*/*"));
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* includes = new ListValue();
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < script.globs().size(); ++i)
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    includes->Append(Value::CreateStringValue(script.globs().at(i)));
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* excludes = new ListValue();
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < script.exclude_globs().size(); ++i)
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    excludes->Append(Value::CreateStringValue(script.exclude_globs().at(i)));
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DictionaryValue* content_script = new DictionaryValue();
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  content_script->Set(keys::kMatches, matches);
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  content_script->Set(keys::kIncludeGlobs, includes);
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  content_script->Set(keys::kExcludeGlobs, excludes);
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  content_script->Set(keys::kJs, js_files);
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* content_scripts = new ListValue();
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  content_scripts->Append(content_script);
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  root->Set(keys::kContentScripts, content_scripts);
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath manifest_path = temp_dir.path().Append(
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Extension::kManifestFilename);
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  JSONFileValueSerializer serializer(manifest_path);
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!serializer.Serialize(*root)) {
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *error = "Could not write JSON.";
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Write the script file.
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!file_util::CopyFile(user_script_path,
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           temp_dir.path().AppendASCII("script.js"))) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *error = "Could not copy script file.";
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
149513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  scoped_refptr<Extension> extension = Extension::Create(
150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      temp_dir.path(),
151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      Extension::INTERNAL,
152dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      *root,
153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Extension::NO_FLAGS,
154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      error);
155513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (!extension) {
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Could not init extension " << *error;
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  temp_dir.Take();  // The caller takes ownership of the directory.
161513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  return extension;
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
163