Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / chrome / common / importer / firefox_importer_utils.cc
blob4e9a8554e0b1b7423a3cd865b1a402de10b7b3fd
1 // Copyright (c) 2012 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.
5 #include "chrome/common/importer/firefox_importer_utils.h"
7 #include <algorithm>
8 #include <map>
9 #include <string>
11 #include "base/files/file_util.h"
12 #include "base/logging.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/common/ini_parser.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "url/gurl.h"
24 namespace {
26 // Retrieves the file system path of the profile name.
27 base::FilePath GetProfilePath(const base::DictionaryValue& root,
28 const std::string& profile_name) {
29 base::string16 path16;
30 std::string is_relative;
31 if (!root.GetStringASCII(profile_name + ".IsRelative", &is_relative) ||
32 !root.GetString(profile_name + ".Path", &path16))
33 return base::FilePath();
35 #if defined(OS_WIN)
36 ReplaceSubstringsAfterOffset(
37 &path16, 0, base::ASCIIToUTF16("/"), base::ASCIIToUTF16("\\"));
38 #endif
39 base::FilePath path = base::FilePath::FromUTF16Unsafe(path16);
41 // IsRelative=1 means the folder path would be relative to the
42 // path of profiles.ini. IsRelative=0 refers to a custom profile
43 // location.
44 if (is_relative == "1")
45 path = GetProfilesINI().DirName().Append(path);
47 return path;
50 // Checks if the named profile is the default profile.
51 bool IsDefaultProfile(const base::DictionaryValue& root,
52 const std::string& profile_name) {
53 std::string is_default;
54 root.GetStringASCII(profile_name + ".Default", &is_default);
55 return is_default == "1";
58 } // namespace
60 base::FilePath GetFirefoxProfilePath() {
61 base::FilePath ini_file = GetProfilesINI();
62 std::string content;
63 base::ReadFileToString(ini_file, &content);
64 DictionaryValueINIParser ini_parser;
65 ini_parser.Parse(content);
66 return GetFirefoxProfilePathFromDictionary(ini_parser.root());
69 base::FilePath GetFirefoxProfilePathFromDictionary(
70 const base::DictionaryValue& root) {
71 std::vector<std::string> profiles;
72 for (int i = 0; ; ++i) {
73 std::string current_profile = base::StringPrintf("Profile%d", i);
74 if (root.HasKey(current_profile)) {
75 profiles.push_back(current_profile);
76 } else {
77 // Profiles are continuously numbered. So we exit when we can't
78 // find the i-th one.
79 break;
83 if (profiles.empty())
84 return base::FilePath();
86 // When multiple profiles exist, the path to the default profile is returned,
87 // since the other profiles are used mostly by developers for testing.
88 for (std::vector<std::string>::const_iterator it = profiles.begin();
89 it != profiles.end(); ++it)
90 if (IsDefaultProfile(root, *it))
91 return GetProfilePath(root, *it);
93 // If no default profile is found, the path to Profile0 will be returned.
94 return GetProfilePath(root, profiles.front());
97 #if defined(OS_MACOSX)
98 // Find the "*.app" component of the path and build up from there.
99 // The resulting path will be .../Firefox.app/Contents/MacOS.
100 // We do this because we don't trust LastAppDir to always be
101 // this particular path, without any subdirs, and we want to make
102 // our assumption about Firefox's root being in that path explicit.
103 bool ComposeMacAppPath(const std::string& path_from_file,
104 base::FilePath* output) {
105 base::FilePath path(path_from_file);
106 typedef std::vector<base::FilePath::StringType> ComponentVector;
107 ComponentVector path_components;
108 path.GetComponents(&path_components);
109 if (path_components.empty())
110 return false;
111 // The first path component is special because it may be absolute. Calling
112 // Append with an absolute path component will trigger an assert, so we
113 // must handle it differently and initialize output with it.
114 *output = base::FilePath(path_components[0]);
115 // Append next path components untill we find the *.app component. When we do,
116 // append Contents/MacOS.
117 for (size_t i = 1; i < path_components.size(); ++i) {
118 *output = output->Append(path_components[i]);
119 if (EndsWith(path_components[i], ".app", true)) {
120 *output = output->Append("Contents");
121 *output = output->Append("MacOS");
122 return true;
125 LOG(ERROR) << path_from_file << " doesn't look like a valid Firefox "
126 << "installation path: missing /*.app/ directory.";
127 return false;
129 #endif // OS_MACOSX
131 bool GetFirefoxVersionAndPathFromProfile(const base::FilePath& profile_path,
132 int* version,
133 base::FilePath* app_path) {
134 bool ret = false;
135 base::FilePath compatibility_file =
136 profile_path.AppendASCII("compatibility.ini");
137 std::string content;
138 base::ReadFileToString(compatibility_file, &content);
139 ReplaceSubstringsAfterOffset(&content, 0, "\r\n", "\n");
140 std::vector<std::string> lines;
141 base::SplitString(content, '\n', &lines);
143 for (size_t i = 0; i < lines.size(); ++i) {
144 const std::string& line = lines[i];
145 if (line.empty() || line[0] == '#' || line[0] == ';')
146 continue;
147 size_t equal = line.find('=');
148 if (equal != std::string::npos) {
149 std::string key = line.substr(0, equal);
150 if (key == "LastVersion") {
151 base::StringToInt(line.substr(equal + 1), version);
152 ret = true;
153 } else if (key == "LastAppDir") {
154 // TODO(evanm): If the path in question isn't convertible to
155 // UTF-8, what does Firefox do? If it puts raw bytes in the
156 // file, we could go straight from bytes -> filepath;
157 // otherwise, we're out of luck here.
158 #if defined(OS_MACOSX)
159 // Extract path from "LastAppDir=/actual/path"
160 size_t separator_pos = line.find_first_of('=');
161 const std::string& path_from_ini = line.substr(separator_pos + 1);
162 if (!ComposeMacAppPath(path_from_ini, app_path))
163 return false;
164 #else // !OS_MACOSX
165 *app_path = base::FilePath::FromUTF8Unsafe(line.substr(equal + 1));
166 #endif // OS_MACOSX
170 return ret;
173 bool ReadPrefFile(const base::FilePath& path, std::string* content) {
174 if (content == NULL)
175 return false;
177 base::ReadFileToString(path, content);
179 if (content->empty()) {
180 LOG(WARNING) << "Firefox preference file " << path.value() << " is empty.";
181 return false;
184 return true;
187 std::string ReadBrowserConfigProp(const base::FilePath& app_path,
188 const std::string& pref_key) {
189 std::string content;
190 if (!ReadPrefFile(app_path.AppendASCII("browserconfig.properties"), &content))
191 return std::string();
193 // This file has the syntax: key=value.
194 size_t prop_index = content.find(pref_key + "=");
195 if (prop_index == std::string::npos)
196 return std::string();
198 size_t start = prop_index + pref_key.length();
199 size_t stop = std::string::npos;
200 if (start != std::string::npos)
201 stop = content.find("\n", start + 1);
203 if (start == std::string::npos ||
204 stop == std::string::npos || (start == stop)) {
205 LOG(WARNING) << "Firefox property " << pref_key << " could not be parsed.";
206 return std::string();
209 return content.substr(start + 1, stop - start - 1);
212 std::string ReadPrefsJsValue(const base::FilePath& profile_path,
213 const std::string& pref_key) {
214 std::string content;
215 if (!ReadPrefFile(profile_path.AppendASCII("prefs.js"), &content))
216 return std::string();
218 return GetPrefsJsValue(content, pref_key);
221 GURL GetHomepage(const base::FilePath& profile_path) {
222 std::string home_page_list =
223 ReadPrefsJsValue(profile_path, "browser.startup.homepage");
225 size_t seperator = home_page_list.find_first_of('|');
226 if (seperator == std::string::npos)
227 return GURL(home_page_list);
229 return GURL(home_page_list.substr(0, seperator));
232 bool IsDefaultHomepage(const GURL& homepage, const base::FilePath& app_path) {
233 if (!homepage.is_valid())
234 return false;
236 std::string default_homepages =
237 ReadBrowserConfigProp(app_path, "browser.startup.homepage");
239 size_t seperator = default_homepages.find_first_of('|');
240 if (seperator == std::string::npos)
241 return homepage.spec() == GURL(default_homepages).spec();
243 // Crack the string into separate homepage urls.
244 std::vector<std::string> urls;
245 base::SplitString(default_homepages, '|', &urls);
247 for (size_t i = 0; i < urls.size(); ++i) {
248 if (homepage.spec() == GURL(urls[i]).spec())
249 return true;
252 return false;
255 std::string GetPrefsJsValue(const std::string& content,
256 const std::string& pref_key) {
257 // This file has the syntax: user_pref("key", value);
258 std::string search_for = std::string("user_pref(\"") + pref_key +
259 std::string("\", ");
260 size_t prop_index = content.find(search_for);
261 if (prop_index == std::string::npos)
262 return std::string();
264 size_t start = prop_index + search_for.length();
265 size_t stop = std::string::npos;
266 if (start != std::string::npos) {
267 // Stop at the last ')' on this line.
268 stop = content.find("\n", start + 1);
269 stop = content.rfind(")", stop);
272 if (start == std::string::npos || stop == std::string::npos ||
273 stop < start) {
274 LOG(WARNING) << "Firefox property " << pref_key << " could not be parsed.";
275 return std::string();
278 // String values have double quotes we don't need to return to the caller.
279 if (content[start] == '\"' && content[stop - 1] == '\"') {
280 ++start;
281 --stop;
284 return content.substr(start, stop - start);
287 // The branding name is obtained from the application.ini file from the Firefox
288 // application directory. A sample application.ini file is the following:
289 // [App]
290 // Vendor=Mozilla
291 // Name=Iceweasel
292 // Profile=mozilla/firefox
293 // Version=3.5.16
294 // BuildID=20120421070307
295 // Copyright=Copyright (c) 1998 - 2010 mozilla.org
296 // ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
297 // .........................................
298 // In this example the function returns "Iceweasel" (or a localized equivalent).
299 base::string16 GetFirefoxImporterName(const base::FilePath& app_path) {
300 const base::FilePath app_ini_file = app_path.AppendASCII("application.ini");
301 std::string branding_name;
302 if (base::PathExists(app_ini_file)) {
303 std::string content;
304 base::ReadFileToString(app_ini_file, &content);
305 std::vector<std::string> lines;
306 base::SplitString(content, '\n', &lines);
307 const std::string name_attr("Name=");
308 bool in_app_section = false;
309 for (size_t i = 0; i < lines.size(); ++i) {
310 base::TrimWhitespace(lines[i], base::TRIM_ALL, &lines[i]);
311 if (lines[i] == "[App]") {
312 in_app_section = true;
313 } else if (in_app_section) {
314 if (lines[i].find(name_attr) == 0) {
315 branding_name = lines[i].substr(name_attr.size());
316 break;
317 } else if (lines[i].length() > 0 && lines[i][0] == '[') {
318 // No longer in the [App] section.
319 break;
325 base::StringToLowerASCII(&branding_name);
326 if (branding_name.find("iceweasel") != std::string::npos)
327 return l10n_util::GetStringUTF16(IDS_IMPORT_FROM_ICEWEASEL);
328 return l10n_util::GetStringUTF16(IDS_IMPORT_FROM_FIREFOX);