1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/external_policy_extension_loader.h"
6
7#include "base/logging.h"
8#include "base/values.h"
9#include "chrome/browser/prefs/pref_service.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/common/extensions/extension.h"
12#include "chrome/common/pref_names.h"
13#include "content/browser/browser_thread.h"
14#include "content/common/notification_service.h"
15#include "content/common/notification_type.h"
16#include "googleurl/src/gurl.h"
17
18namespace {
19
20// Check an extension ID and an URL to be syntactically correct.
21bool CheckExtension(const std::string& id, const std::string& update_url) {
22  GURL url(update_url);
23  if (!url.is_valid()) {
24    LOG(WARNING) << "Policy specifies invalid update URL for external "
25                 << "extension: " << update_url;
26    return false;
27  }
28  if (!Extension::IdIsValid(id)) {
29    LOG(WARNING) << "Policy specifies invalid ID for external "
30                 << "extension: " << id;
31    return false;
32  }
33  return true;
34}
35
36}  // namespace
37
38ExternalPolicyExtensionLoader::ExternalPolicyExtensionLoader(
39    Profile* profile)
40    : profile_(profile) {
41  pref_change_registrar_.Init(profile_->GetPrefs());
42  pref_change_registrar_.Add(prefs::kExtensionInstallForceList, this);
43  notification_registrar_.Add(this,
44                              NotificationType::PROFILE_DESTROYED,
45                              Source<Profile>(profile_));
46}
47
48void ExternalPolicyExtensionLoader::StartLoading() {
49  const ListValue* forcelist =
50      profile_->GetPrefs()->GetList(prefs::kExtensionInstallForceList);
51  DictionaryValue* result = new DictionaryValue();
52  if (forcelist != NULL) {
53    std::string extension_desc;
54    for (ListValue::const_iterator it = forcelist->begin();
55         it != forcelist->end(); ++it) {
56      if (!(*it)->GetAsString(&extension_desc)) {
57        LOG(WARNING) << "Failed to read forcelist string.";
58      } else {
59        // Each string item of the list has the following form:
60        // extension_id_code;extension_update_url
61        // The update URL might also contain semicolons.
62        size_t pos = extension_desc.find(';');
63        std::string id = extension_desc.substr(0, pos);
64        std::string update_url = extension_desc.substr(pos+1);
65        if (CheckExtension(id, update_url)) {
66          result->SetString(id + ".external_update_url", update_url);
67        }
68      }
69    }
70  }
71  prefs_.reset(result);
72  LoadFinished();
73}
74
75void ExternalPolicyExtensionLoader::Observe(
76    NotificationType type,
77    const NotificationSource& source,
78    const NotificationDetails& details) {
79  if (profile_ == NULL) return;
80  switch (type.value) {
81    case NotificationType::PREF_CHANGED: {
82      if (Source<PrefService>(source).ptr() == profile_->GetPrefs()) {
83        std::string* pref_name = Details<std::string>(details).ptr();
84        if (*pref_name == prefs::kExtensionInstallForceList) {
85          StartLoading();
86        } else {
87          NOTREACHED() << "Unexpected preference name.";
88        }
89      }
90      break;
91    }
92    case NotificationType::PROFILE_DESTROYED: {
93      if (Source<Profile>(source).ptr() == profile_) {
94        notification_registrar_.RemoveAll();
95        pref_change_registrar_.RemoveAll();
96        profile_ = NULL;
97      }
98      break;
99    }
100    default:
101      NOTREACHED() << "Unexpected notification type.";
102  }
103}
104