• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/supervised_user/supervised_user_site_list.h"
6 
7 #include "base/json/json_file_value_serializer.h"
8 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "extensions/common/extension.h"
12 
13 using base::DictionaryValue;
14 using base::ListValue;
15 using base::Value;
16 
17 const int kSitelistFormatVersion = 1;
18 
19 const char kCategoriesKey[] = "categories";
20 const char kHostnameHashesKey[] = "hostname_hashes";
21 const char kNameKey[] = "name";
22 const char kSitesKey[] = "sites";
23 const char kSitelistFormatVersionKey[] = "version";
24 const char kThumbnailKey[] = "thumbnail";
25 const char kThumbnailUrlKey[] = "thumbnail_url";
26 const char kUrlKey[] = "url";
27 const char kWhitelistKey[] = "whitelist";
28 
29 namespace {
30 
31 struct CategoryInfo {
32   const char* identifier;
33   const char* name;
34 };
35 
36 // These are placeholders for now.
37 CategoryInfo g_categories[] = {
38   {"com.google.chrome.animals", "Animals and Plants"},
39   {"com.google.chrome.arts", "Arts"},
40   {"com.google.chrome.business", "Business"},
41   {"com.google.chrome.computers", "Computers"},
42   {"com.google.chrome.education", "Education"},
43   {"com.google.chrome.entertainment", "Entertainment"},
44   {"com.google.chrome.games", "Games"},
45   {"com.google.chrome.health", "Health"},
46   {"com.google.chrome.home", "Home"},
47   {"com.google.chrome.international", "International"},
48   {"com.google.chrome.news", "News"},
49   {"com.google.chrome.people", "People and Society"},
50   {"com.google.chrome.places", "Places"},
51   {"com.google.chrome.pre-school", "Pre-School"},
52   {"com.google.chrome.reference", "Reference"},
53   {"com.google.chrome.science", "Science"},
54   {"com.google.chrome.shopping", "Shopping"},
55   {"com.google.chrome.sports", "Sports and Hobbies"},
56   {"com.google.chrome.teens", "Teens"}
57 };
58 
59 // Category 0 is "not listed"; actual category IDs start at 1.
GetCategoryId(const std::string & category)60 int GetCategoryId(const std::string& category) {
61   for (size_t i = 0; i < arraysize(g_categories); ++i) {
62     if (g_categories[i].identifier == category)
63       return i + 1;
64   }
65   return 0;
66 }
67 
68 // Takes a DictionaryValue entry from the JSON file and fills the whitelist
69 // (via URL patterns or hostname hashes) and the URL in the corresponding Site
70 // struct.
AddWhitelistEntries(const base::DictionaryValue * site_dict,SupervisedUserSiteList::Site * site)71 void AddWhitelistEntries(const base::DictionaryValue* site_dict,
72                          SupervisedUserSiteList::Site* site) {
73   std::vector<std::string>* patterns = &site->patterns;
74 
75   bool found = false;
76   const base::ListValue* whitelist = NULL;
77   if (site_dict->GetList(kWhitelistKey, &whitelist)) {
78     found = true;
79     for (base::ListValue::const_iterator whitelist_it = whitelist->begin();
80          whitelist_it != whitelist->end(); ++whitelist_it) {
81       std::string pattern;
82       if (!(*whitelist_it)->GetAsString(&pattern)) {
83         LOG(ERROR) << "Invalid whitelist entry";
84         continue;
85       }
86 
87       patterns->push_back(pattern);
88     }
89   }
90 
91   std::vector<std::string>* hashes = &site->hostname_hashes;
92   const base::ListValue* hash_list = NULL;
93   if (site_dict->GetList(kHostnameHashesKey, &hash_list)) {
94     found = true;
95     for (base::ListValue::const_iterator hash_list_it = hash_list->begin();
96          hash_list_it != hash_list->end(); ++hash_list_it) {
97       std::string hash;
98       if (!(*hash_list_it)->GetAsString(&hash)) {
99         LOG(ERROR) << "Invalid whitelist entry";
100         continue;
101       }
102 
103       hashes->push_back(hash);
104     }
105   }
106 
107   if (found)
108     return;
109 
110   // Fall back to using a whitelist based on the URL.
111   std::string url_str;
112   if (!site_dict->GetString(kUrlKey, &url_str)) {
113     LOG(ERROR) << "Whitelist is invalid";
114     return;
115   }
116 
117   GURL url(url_str);
118   if (!url.is_valid()) {
119     LOG(ERROR) << "URL " << url_str << " is invalid";
120     return;
121   }
122 
123   patterns->push_back(url.host());
124 }
125 
126 }  // namespace
127 
Site(const base::string16 & name,int category_id)128 SupervisedUserSiteList::Site::Site(const base::string16& name,
129                                    int category_id)
130     : name(name),
131       category_id(category_id) {}
132 
~Site()133 SupervisedUserSiteList::Site::~Site() {}
134 
SupervisedUserSiteList(const std::string & extension_id,const base::FilePath & path)135 SupervisedUserSiteList::SupervisedUserSiteList(
136     const std::string& extension_id,
137     const base::FilePath& path)
138     : extension_id_(extension_id),
139       path_(path) {
140 }
141 
~SupervisedUserSiteList()142 SupervisedUserSiteList::~SupervisedUserSiteList() {
143 }
144 
Clone()145 SupervisedUserSiteList* SupervisedUserSiteList::Clone() {
146   return new SupervisedUserSiteList(extension_id_, path_);
147 }
148 
149 // static
GetCategoryNames(std::vector<base::string16> * categories)150 void SupervisedUserSiteList::GetCategoryNames(
151     std::vector<base::string16>* categories) {
152   // TODO(bauerb): Collect custom categories from extensions.
153   for (size_t i = 0; i < arraysize(g_categories); ++i) {
154     categories->push_back(base::ASCIIToUTF16(g_categories[i].name));
155   }
156 }
157 
GetSites(std::vector<Site> * sites)158 void SupervisedUserSiteList::GetSites(std::vector<Site>* sites) {
159   if (!LazyLoad())
160     return;
161 
162   for (base::ListValue::iterator entry_it = sites_->begin();
163        entry_it != sites_->end(); ++entry_it) {
164     base::DictionaryValue* entry = NULL;
165     if (!(*entry_it)->GetAsDictionary(&entry)) {
166       LOG(ERROR) << "Entry is invalid";
167       continue;
168     }
169 
170     base::string16 name;
171     entry->GetString(kNameKey, &name);
172 
173     // TODO(bauerb): We need to distinguish between "no category assigned" and
174     // "not on any site list".
175     int category_id = 0;
176     const base::ListValue* categories = NULL;
177     if (entry->GetList(kCategoriesKey, &categories)) {
178       for (base::ListValue::const_iterator it = categories->begin();
179            it != categories->end(); ++it) {
180         std::string category;
181         if (!(*it)->GetAsString(&category)) {
182           LOG(ERROR) << "Invalid category";
183           continue;
184         }
185         category_id = GetCategoryId(category);
186         break;
187       }
188     }
189     sites->push_back(Site(name, category_id));
190     AddWhitelistEntries(entry, &sites->back());
191   }
192 }
193 
LazyLoad()194 bool SupervisedUserSiteList::LazyLoad() {
195   if (sites_.get())
196     return true;
197 
198   JSONFileValueSerializer serializer(path_);
199   std::string error;
200   scoped_ptr<base::Value> value(serializer.Deserialize(NULL, &error));
201   if (!value.get()) {
202     LOG(ERROR) << "Couldn't load site list " << path_.value() << ": "
203                << error;
204     return false;
205   }
206 
207   base::DictionaryValue* dict = NULL;
208   if (!value->GetAsDictionary(&dict)) {
209     LOG(ERROR) << "Site list " << path_.value() << " is invalid";
210     return false;
211   }
212 
213   int version = 0;
214   if (!dict->GetInteger(kSitelistFormatVersionKey, &version)) {
215     LOG(ERROR) << "Site list " << path_.value() << " has invalid version";
216     return false;
217   }
218 
219   if (version > kSitelistFormatVersion) {
220     LOG(ERROR) << "Site list " << path_.value() << " has a too new format";
221     return false;
222   }
223 
224   base::ListValue* sites = NULL;
225   if (dict->GetList(kSitesKey, &sites))
226     sites_.reset(sites->DeepCopy());
227 
228   base::DictionaryValue* categories = NULL;
229   if (dict->GetDictionary(kCategoriesKey, &categories))
230     categories_.reset(categories->DeepCopy());
231 
232   return true;
233 }
234 
CopyThumbnailUrl(const base::DictionaryValue * source,base::DictionaryValue * dest)235 void SupervisedUserSiteList::CopyThumbnailUrl(
236     const base::DictionaryValue* source,
237     base::DictionaryValue* dest) {
238   if (!source->HasKey(kThumbnailKey))
239     return;
240 
241   std::string thumbnail;
242   if (!source->GetString(kThumbnailKey, &thumbnail)) {
243     LOG(ERROR) << "Invalid thumbnail";
244     return;
245   }
246 
247   GURL base_url =
248       extensions::Extension::GetBaseURLFromExtensionId(extension_id_);
249   GURL thumbnail_url = base_url.Resolve(thumbnail);
250   if (!thumbnail_url.is_valid()) {
251     LOG(ERROR) << "Invalid thumbnail";
252     return;
253   }
254 
255   dest->SetString(kThumbnailUrlKey, thumbnail_url.spec());
256 }
257