1// Copyright 2014 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/pending_extension_manager.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "base/version.h"
11#include "chrome/common/extensions/extension_constants.h"
12#include "content/public/browser/browser_thread.h"
13#include "extensions/browser/extension_prefs.h"
14#include "extensions/browser/extension_registry.h"
15#include "extensions/common/constants.h"
16#include "extensions/common/extension.h"
17#include "url/gurl.h"
18
19using content::BrowserThread;
20
21namespace {
22
23// Install predicate used by AddFromExternalUpdateUrl().
24bool AlwaysInstall(const extensions::Extension* extension) {
25  return true;
26}
27
28std::string GetVersionString(const Version& version) {
29  return version.IsValid() ? version.GetString() : "invalid";
30}
31
32}  // namespace
33
34namespace extensions {
35
36PendingExtensionManager::PendingExtensionManager(
37    content::BrowserContext* context)
38    : context_(context) {}
39
40PendingExtensionManager::~PendingExtensionManager() {}
41
42const PendingExtensionInfo* PendingExtensionManager::GetById(
43    const std::string& id) const {
44  PendingExtensionList::const_iterator iter;
45  for (iter = pending_extension_list_.begin();
46       iter != pending_extension_list_.end();
47       ++iter) {
48    if (id == iter->id())
49      return &(*iter);
50  }
51
52  return NULL;
53}
54
55bool PendingExtensionManager::Remove(const std::string& id) {
56  PendingExtensionList::iterator iter;
57  for (iter = pending_extension_list_.begin();
58       iter != pending_extension_list_.end();
59       ++iter) {
60    if (id == iter->id()) {
61      pending_extension_list_.erase(iter);
62      return true;
63    }
64  }
65
66  return false;
67}
68
69bool PendingExtensionManager::IsIdPending(const std::string& id) const {
70  return GetById(id) != NULL;
71}
72
73bool PendingExtensionManager::HasPendingExtensions() const {
74  return !pending_extension_list_.empty();
75}
76
77bool PendingExtensionManager::HasPendingExtensionFromSync() const {
78  PendingExtensionList::const_iterator iter;
79  for (iter = pending_extension_list_.begin();
80       iter != pending_extension_list_.end();
81       ++iter) {
82    if (iter->is_from_sync())
83      return true;
84  }
85
86  return false;
87}
88
89bool PendingExtensionManager::AddFromSync(
90    const std::string& id,
91    const GURL& update_url,
92    PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
93    bool install_silently,
94    bool remote_install,
95    bool installed_by_custodian) {
96  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
97
98  if (ExtensionRegistry::Get(context_)->GetExtensionById(
99          id, ExtensionRegistry::EVERYTHING)) {
100    LOG(ERROR) << "Trying to add pending extension " << id
101               << " which already exists";
102    return false;
103  }
104
105  // Make sure we don't ever try to install the CWS app, because even though
106  // it is listed as a syncable app (because its values need to be synced) it
107  // should already be installed on every instance.
108  if (id == extensions::kWebStoreAppId) {
109    NOTREACHED();
110    return false;
111  }
112
113  int creation_flags = Extension::NO_FLAGS;
114  if (installed_by_custodian) {
115    creation_flags |= Extension::WAS_INSTALLED_BY_CUSTODIAN;
116  }
117
118  static const bool kIsFromSync = true;
119  static const Manifest::Location kSyncLocation = Manifest::INTERNAL;
120  static const bool kMarkAcknowledged = false;
121
122  return AddExtensionImpl(id,
123                          std::string(),
124                          update_url,
125                          Version(),
126                          should_allow_install,
127                          kIsFromSync,
128                          install_silently,
129                          kSyncLocation,
130                          creation_flags,
131                          kMarkAcknowledged,
132                          remote_install);
133}
134
135bool PendingExtensionManager::AddFromExtensionImport(
136    const std::string& id,
137    const GURL& update_url,
138    PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install) {
139  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140
141  if (ExtensionRegistry::Get(context_)->GetExtensionById(
142          id, ExtensionRegistry::EVERYTHING)) {
143    LOG(ERROR) << "Trying to add pending extension " << id
144               << " which already exists";
145    return false;
146  }
147
148  static const bool kIsFromSync = false;
149  static const bool kInstallSilently = true;
150  static const Manifest::Location kManifestLocation = Manifest::INTERNAL;
151  static const bool kMarkAcknowledged = false;
152  static const bool kRemoteInstall = false;
153
154  return AddExtensionImpl(id,
155                          std::string(),
156                          update_url,
157                          Version(),
158                          should_allow_install,
159                          kIsFromSync,
160                          kInstallSilently,
161                          kManifestLocation,
162                          Extension::NO_FLAGS,
163                          kMarkAcknowledged,
164                          kRemoteInstall);
165}
166
167bool PendingExtensionManager::AddFromExternalUpdateUrl(
168    const std::string& id,
169    const std::string& install_parameter,
170    const GURL& update_url,
171    Manifest::Location location,
172    int creation_flags,
173    bool mark_acknowledged) {
174  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175
176  static const bool kIsFromSync = false;
177  static const bool kInstallSilently = true;
178  static const bool kRemoteInstall = false;
179
180  const Extension* extension = ExtensionRegistry::Get(context_)
181      ->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
182  if (extension && location == Manifest::GetHigherPriorityLocation(
183                                   location, extension->location())) {
184    // If the new location has higher priority than the location of an existing
185    // extension, let the update process overwrite the existing extension.
186  } else {
187    if (ExtensionPrefs::Get(context_)->IsExternalExtensionUninstalled(id))
188      return false;
189
190    if (extension) {
191      LOG(DFATAL) << "Trying to add extension " << id
192                  << " by external update, but it is already installed.";
193      return false;
194    }
195  }
196
197  return AddExtensionImpl(id,
198                          install_parameter,
199                          update_url,
200                          Version(),
201                          &AlwaysInstall,
202                          kIsFromSync,
203                          kInstallSilently,
204                          location,
205                          creation_flags,
206                          mark_acknowledged,
207                          kRemoteInstall);
208}
209
210
211bool PendingExtensionManager::AddFromExternalFile(
212    const std::string& id,
213    Manifest::Location install_source,
214    const Version& version,
215    int creation_flags,
216    bool mark_acknowledged) {
217  // TODO(skerner): AddFromSync() checks to see if the extension is
218  // installed, but this method assumes that the caller already
219  // made sure it is not installed.  Make all AddFrom*() methods
220  // consistent.
221  const GURL& kUpdateUrl = GURL::EmptyGURL();
222  static const bool kIsFromSync = false;
223  static const bool kInstallSilently = true;
224  static const bool kRemoteInstall = false;
225
226  return AddExtensionImpl(id,
227                          std::string(),
228                          kUpdateUrl,
229                          version,
230                          &AlwaysInstall,
231                          kIsFromSync,
232                          kInstallSilently,
233                          install_source,
234                          creation_flags,
235                          mark_acknowledged,
236                          kRemoteInstall);
237}
238
239void PendingExtensionManager::GetPendingIdsForUpdateCheck(
240    std::list<std::string>* out_ids_for_update_check) const {
241  PendingExtensionList::const_iterator iter;
242  for (iter = pending_extension_list_.begin();
243       iter != pending_extension_list_.end();
244       ++iter) {
245    Manifest::Location install_source = iter->install_source();
246
247    // Some install sources read a CRX from the filesystem.  They can
248    // not be fetched from an update URL, so don't include them in the
249    // set of ids.
250    if (install_source == Manifest::EXTERNAL_PREF ||
251        install_source == Manifest::EXTERNAL_REGISTRY)
252      continue;
253
254    out_ids_for_update_check->push_back(iter->id());
255  }
256}
257
258bool PendingExtensionManager::AddExtensionImpl(
259    const std::string& id,
260    const std::string& install_parameter,
261    const GURL& update_url,
262    const Version& version,
263    PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
264    bool is_from_sync,
265    bool install_silently,
266    Manifest::Location install_source,
267    int creation_flags,
268    bool mark_acknowledged,
269    bool remote_install) {
270  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272  PendingExtensionInfo info(id,
273                            install_parameter,
274                            update_url,
275                            version,
276                            should_allow_install,
277                            is_from_sync,
278                            install_silently,
279                            install_source,
280                            creation_flags,
281                            mark_acknowledged,
282                            remote_install);
283
284  if (const PendingExtensionInfo* pending = GetById(id)) {
285    // Bugs in this code will manifest as sporadic incorrect extension
286    // locations in situations where multiple install sources run at the
287    // same time. For example, on first login to a chrome os machine, an
288    // extension may be requested by sync and the default extension set.
289    // The following logging will help diagnose such issues.
290    VLOG(1) << "Extension id " << id
291            << " was entered for update more than once."
292            << "  old location: " << pending->install_source()
293            << "  new location: " << install_source
294            << "  old version: " << GetVersionString(pending->version())
295            << "  new version: " << GetVersionString(version);
296
297    // Never override an existing extension with an older version. Only
298    // extensions from local CRX files have a known version; extensions from an
299    // update URL will get the latest version.
300
301    // If |pending| has the same or higher precedence than |info| then don't
302    // install |info| over |pending|.
303    if (pending->CompareTo(info) >= 0)
304      return false;
305
306    VLOG(1) << "Overwrite existing record.";
307
308    std::replace(pending_extension_list_.begin(),
309                 pending_extension_list_.end(),
310                 *pending,
311                 info);
312  } else {
313    pending_extension_list_.push_back(info);
314  }
315
316  return true;
317}
318
319void PendingExtensionManager::AddForTesting(
320    const PendingExtensionInfo& pending_extension_info) {
321  pending_extension_list_.push_back(pending_extension_info);
322}
323
324}  // namespace extensions
325