Don't compile save_password_infobar_delegate.cc everywhere but Mac and Android
[chromium-blink-merge.git] / chrome / common / importer / firefox_importer_utils.cc
blob4dfb76e6c543d743742bc14dbc3bd7ea23aae8a4
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 base::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 (base::EndsWith(path_components[i], ".app",
120 base::CompareCase::SENSITIVE)) {
121 *output = output->Append("Contents");
122 *output = output->Append("MacOS");
123 return true;
126 LOG(ERROR) << path_from_file << " doesn't look like a valid Firefox "
127 << "installation path: missing /*.app/ directory.";
128 return false;
130 #endif // OS_MACOSX
132 bool GetFirefoxVersionAndPathFromProfile(const base::FilePath& profile_path,
133 int* version,
134 base::FilePath* app_path) {
135 bool ret = false;
136 base::FilePath compatibility_file =
137 profile_path.AppendASCII("compatibility.ini");
138 std::string content;
139 base::ReadFileToString(compatibility_file, &content);
140 base::ReplaceSubstringsAfterOffset(&content, 0, "\r\n", "\n");
142 for (const std::string& line : base::SplitString(
143 content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
144 if (line.empty() || line[0] == '#' || line[0] == ';')
145 continue;
146 size_t equal = line.find('=');
147 if (equal != std::string::npos) {
148 std::string key = line.substr(0, equal);
149 if (key == "LastVersion") {
150 base::StringToInt(line.substr(equal + 1), version);
151 ret = true;
152 } else if (key == "LastAppDir") {
153 // TODO(evanm): If the path in question isn't convertible to
154 // UTF-8, what does Firefox do? If it puts raw bytes in the
155 // file, we could go straight from bytes -> filepath;
156 // otherwise, we're out of luck here.
157 #if defined(OS_MACOSX)
158 // Extract path from "LastAppDir=/actual/path"
159 size_t separator_pos = line.find_first_of('=');
160 const std::string& path_from_ini = line.substr(separator_pos + 1);
161 if (!ComposeMacAppPath(path_from_ini, app_path))
162 return false;
163 #else // !OS_MACOSX
164 *app_path = base::FilePath::FromUTF8Unsafe(line.substr(equal + 1));
165 #endif // OS_MACOSX
169 return ret;
172 bool ReadPrefFile(const base::FilePath& path, std::string* content) {
173 if (content == NULL)
174 return false;
176 base::ReadFileToString(path, content);
178 if (content->empty()) {
179 LOG(WARNING) << "Firefox preference file " << path.value() << " is empty.";
180 return false;
183 return true;
186 std::string ReadBrowserConfigProp(const base::FilePath& app_path,
187 const std::string& pref_key) {
188 std::string content;
189 if (!ReadPrefFile(app_path.AppendASCII("browserconfig.properties"), &content))
190 return std::string();
192 // This file has the syntax: key=value.
193 size_t prop_index = content.find(pref_key + "=");
194 if (prop_index == std::string::npos)
195 return std::string();
197 size_t start = prop_index + pref_key.length();
198 size_t stop = std::string::npos;
199 if (start != std::string::npos)
200 stop = content.find("\n", start + 1);
202 if (start == std::string::npos ||
203 stop == std::string::npos || (start == stop)) {
204 LOG(WARNING) << "Firefox property " << pref_key << " could not be parsed.";
205 return std::string();
208 return content.substr(start + 1, stop - start - 1);
211 std::string ReadPrefsJsValue(const base::FilePath& profile_path,
212 const std::string& pref_key) {
213 std::string content;
214 if (!ReadPrefFile(profile_path.AppendASCII("prefs.js"), &content))
215 return std::string();
217 return GetPrefsJsValue(content, pref_key);
220 GURL GetHomepage(const base::FilePath& profile_path) {
221 std::string home_page_list =
222 ReadPrefsJsValue(profile_path, "browser.startup.homepage");
224 size_t seperator = home_page_list.find_first_of('|');
225 if (seperator == std::string::npos)
226 return GURL(home_page_list);
228 return GURL(home_page_list.substr(0, seperator));
231 bool IsDefaultHomepage(const GURL& homepage, const base::FilePath& app_path) {
232 if (!homepage.is_valid())
233 return false;
235 std::string default_homepages =
236 ReadBrowserConfigProp(app_path, "browser.startup.homepage");
238 size_t seperator = default_homepages.find_first_of('|');
239 if (seperator == std::string::npos)
240 return homepage.spec() == GURL(default_homepages).spec();
242 // Crack the string into separate homepage urls.
243 for (const std::string& url : base::SplitString(
244 default_homepages, "|",
245 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
246 if (homepage.spec() == GURL(url).spec())
247 return true;
250 return false;
253 std::string GetPrefsJsValue(const std::string& content,
254 const std::string& pref_key) {
255 // This file has the syntax: user_pref("key", value);
256 std::string search_for = std::string("user_pref(\"") + pref_key +
257 std::string("\", ");
258 size_t prop_index = content.find(search_for);
259 if (prop_index == std::string::npos)
260 return std::string();
262 size_t start = prop_index + search_for.length();
263 size_t stop = std::string::npos;
264 if (start != std::string::npos) {
265 // Stop at the last ')' on this line.
266 stop = content.find("\n", start + 1);
267 stop = content.rfind(")", stop);
270 if (start == std::string::npos || stop == std::string::npos ||
271 stop < start) {
272 LOG(WARNING) << "Firefox property " << pref_key << " could not be parsed.";
273 return std::string();
276 // String values have double quotes we don't need to return to the caller.
277 if (content[start] == '\"' && content[stop - 1] == '\"') {
278 ++start;
279 --stop;
282 return content.substr(start, stop - start);
285 // The branding name is obtained from the application.ini file from the Firefox
286 // application directory. A sample application.ini file is the following:
287 // [App]
288 // Vendor=Mozilla
289 // Name=Iceweasel
290 // Profile=mozilla/firefox
291 // Version=3.5.16
292 // BuildID=20120421070307
293 // Copyright=Copyright (c) 1998 - 2010 mozilla.org
294 // ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
295 // .........................................
296 // In this example the function returns "Iceweasel" (or a localized equivalent).
297 base::string16 GetFirefoxImporterName(const base::FilePath& app_path) {
298 const base::FilePath app_ini_file = app_path.AppendASCII("application.ini");
299 std::string branding_name;
300 if (base::PathExists(app_ini_file)) {
301 std::string content;
302 base::ReadFileToString(app_ini_file, &content);
304 const std::string name_attr("Name=");
305 bool in_app_section = false;
306 for (const base::StringPiece& line : base::SplitStringPiece(
307 content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
308 if (line == "[App]") {
309 in_app_section = true;
310 } else if (in_app_section) {
311 if (line.find(name_attr) == 0) {
312 line.substr(name_attr.size()).CopyToString(&branding_name);
313 break;
314 } else if (line.length() > 0 && line[0] == '[') {
315 // No longer in the [App] section.
316 break;
322 branding_name = base::ToLowerASCII(branding_name);
323 if (branding_name.find("iceweasel") != std::string::npos)
324 return l10n_util::GetStringUTF16(IDS_IMPORT_FROM_ICEWEASEL);
325 return l10n_util::GetStringUTF16(IDS_IMPORT_FROM_FIREFOX);