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/browser/chromeos/drive/file_system_util.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/i18n/icu_string_conversions.h"
16 #include "base/json/json_file_value_serializer.h"
17 #include "base/logging.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/message_loop/message_loop_proxy.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/threading/sequenced_worker_pool.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chromeos/drive/drive.pb.h"
27 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
28 #include "chrome/browser/chromeos/drive/file_system_interface.h"
29 #include "chrome/browser/chromeos/drive/job_list.h"
30 #include "chrome/browser/chromeos/drive/write_on_cache_file.h"
31 #include "chrome/browser/chromeos/profiles/profile_helper.h"
32 #include "chrome/browser/chromeos/profiles/profile_util.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/profiles/profile_manager.h"
35 #include "chrome/common/chrome_constants.h"
36 #include "chrome/common/chrome_paths_internal.h"
37 #include "chrome/common/pref_names.h"
38 #include "chromeos/chromeos_constants.h"
39 #include "components/user_manager/user_manager.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "net/base/escape.h"
42 #include "storage/browser/fileapi/file_system_url.h"
44 using content::BrowserThread
;
51 std::string
ReadStringFromGDocFile(const base::FilePath
& file_path
,
52 const std::string
& key
) {
53 const int64 kMaxGDocSize
= 4096;
55 if (!base::GetFileSize(file_path
, &file_size
) ||
56 file_size
> kMaxGDocSize
) {
57 LOG(WARNING
) << "File too large to be a GDoc file " << file_path
.value();
61 JSONFileValueDeserializer
reader(file_path
);
62 std::string error_message
;
63 scoped_ptr
<base::Value
> root_value(reader
.Deserialize(NULL
, &error_message
));
65 LOG(WARNING
) << "Failed to parse " << file_path
.value() << " as JSON."
66 << " error = " << error_message
;
70 base::DictionaryValue
* dictionary_value
= NULL
;
72 if (!root_value
->GetAsDictionary(&dictionary_value
) ||
73 !dictionary_value
->GetString(key
, &result
)) {
74 LOG(WARNING
) << "No value for the given key is stored in "
75 << file_path
.value() << ". key = " << key
;
82 // Returns DriveIntegrationService instance, if Drive is enabled.
84 DriveIntegrationService
* GetIntegrationServiceByProfile(Profile
* profile
) {
85 DriveIntegrationService
* service
=
86 DriveIntegrationServiceFactory::FindForProfile(profile
);
87 if (!service
|| !service
->IsMounted())
94 const base::FilePath
& GetDriveGrandRootPath() {
95 CR_DEFINE_STATIC_LOCAL(base::FilePath
, grand_root_path
,
96 (kDriveGrandRootDirName
));
97 return grand_root_path
;
100 const base::FilePath
& GetDriveMyDriveRootPath() {
101 CR_DEFINE_STATIC_LOCAL(base::FilePath
, drive_root_path
,
102 (FILE_PATH_LITERAL("drive/root")));
103 return drive_root_path
;
106 base::FilePath
GetDriveMountPointPathForUserIdHash(
107 const std::string user_id_hash
) {
108 static const base::FilePath::CharType kSpecialMountPointRoot
[] =
109 FILE_PATH_LITERAL("/special");
110 static const char kDriveMountPointNameBase
[] = "drive";
111 return base::FilePath(kSpecialMountPointRoot
).AppendASCII(
112 net::EscapeQueryParamValue(
113 kDriveMountPointNameBase
+
114 (user_id_hash
.empty() ? "" : "-" + user_id_hash
), false));
117 base::FilePath
GetDriveMountPointPath(Profile
* profile
) {
118 std::string id
= chromeos::ProfileHelper::GetUserIdHashFromProfile(profile
);
119 if (id
.empty() || id
== chrome::kLegacyProfileDir
) {
120 // ProfileHelper::GetUserIdHashFromProfile works only when multi-profile is
121 // enabled. In that case, we fall back to use UserManager (it basically just
122 // returns currently active users's hash in such a case.) I still try
123 // ProfileHelper first because it works better in tests.
124 const user_manager::User
* const user
=
125 user_manager::UserManager::IsInitialized()
126 ? chromeos::ProfileHelper::Get()->GetUserByProfile(
127 profile
->GetOriginalProfile())
130 id
= user
->username_hash();
132 return GetDriveMountPointPathForUserIdHash(id
);
135 FileSystemInterface
* GetFileSystemByProfile(Profile
* profile
) {
136 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
138 DriveIntegrationService
* integration_service
=
139 GetIntegrationServiceByProfile(profile
);
140 return integration_service
? integration_service
->file_system() : NULL
;
143 FileSystemInterface
* GetFileSystemByProfileId(void* profile_id
) {
144 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
146 // |profile_id| needs to be checked with ProfileManager::IsValidProfile
148 Profile
* profile
= reinterpret_cast<Profile
*>(profile_id
);
149 if (!g_browser_process
->profile_manager()->IsValidProfile(profile
))
151 return GetFileSystemByProfile(profile
);
154 DriveAppRegistry
* GetDriveAppRegistryByProfile(Profile
* profile
) {
155 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
157 DriveIntegrationService
* integration_service
=
158 GetIntegrationServiceByProfile(profile
);
159 return integration_service
?
160 integration_service
->drive_app_registry() :
164 DriveServiceInterface
* GetDriveServiceByProfile(Profile
* profile
) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
167 DriveIntegrationService
* integration_service
=
168 GetIntegrationServiceByProfile(profile
);
169 return integration_service
? integration_service
->drive_service() : NULL
;
172 bool IsUnderDriveMountPoint(const base::FilePath
& path
) {
173 return !ExtractDrivePath(path
).empty();
176 base::FilePath
ExtractDrivePath(const base::FilePath
& path
) {
177 std::vector
<base::FilePath::StringType
> components
;
178 path
.GetComponents(&components
);
179 if (components
.size() < 3)
180 return base::FilePath();
181 if (components
[0] != FILE_PATH_LITERAL("/"))
182 return base::FilePath();
183 if (components
[1] != FILE_PATH_LITERAL("special"))
184 return base::FilePath();
185 if (!StartsWithASCII(components
[2], "drive", true))
186 return base::FilePath();
188 base::FilePath drive_path
= GetDriveGrandRootPath();
189 for (size_t i
= 3; i
< components
.size(); ++i
)
190 drive_path
= drive_path
.Append(components
[i
]);
194 Profile
* ExtractProfileFromPath(const base::FilePath
& path
) {
195 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
197 const std::vector
<Profile
*>& profiles
=
198 g_browser_process
->profile_manager()->GetLoadedProfiles();
199 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
200 Profile
* original_profile
= profiles
[i
]->GetOriginalProfile();
201 if (original_profile
== profiles
[i
] &&
202 !chromeos::ProfileHelper::IsSigninProfile(original_profile
)) {
203 const base::FilePath base
= GetDriveMountPointPath(original_profile
);
204 if (base
== path
|| base
.IsParent(path
))
205 return original_profile
;
211 base::FilePath
ExtractDrivePathFromFileSystemUrl(
212 const storage::FileSystemURL
& url
) {
213 if (!url
.is_valid() || url
.type() != storage::kFileSystemTypeDrive
)
214 return base::FilePath();
215 return ExtractDrivePath(url
.path());
218 base::FilePath
GetCacheRootPath(Profile
* profile
) {
219 base::FilePath cache_base_path
;
220 chrome::GetUserCacheDirectory(profile
->GetPath(), &cache_base_path
);
221 base::FilePath cache_root_path
=
222 cache_base_path
.Append(chromeos::kDriveCacheDirname
);
223 static const base::FilePath::CharType kFileCacheVersionDir
[] =
224 FILE_PATH_LITERAL("v1");
225 return cache_root_path
.Append(kFileCacheVersionDir
);
228 std::string
EscapeCacheFileName(const std::string
& filename
) {
229 // This is based on net/base/escape.cc: net::(anonymous namespace)::Escape
231 for (size_t i
= 0; i
< filename
.size(); ++i
) {
232 char c
= filename
[i
];
233 if (c
== '%' || c
== '.' || c
== '/') {
234 base::StringAppendF(&escaped
, "%%%02X", c
);
236 escaped
.push_back(c
);
242 std::string
UnescapeCacheFileName(const std::string
& filename
) {
243 std::string unescaped
;
244 for (size_t i
= 0; i
< filename
.size(); ++i
) {
245 char c
= filename
[i
];
246 if (c
== '%' && i
+ 2 < filename
.length()) {
247 c
= (HexDigitToInt(filename
[i
+ 1]) << 4) +
248 HexDigitToInt(filename
[i
+ 2]);
251 unescaped
.push_back(c
);
256 std::string
NormalizeFileName(const std::string
& input
) {
257 DCHECK(base::IsStringUTF8(input
));
260 if (!base::ConvertToUtf8AndNormalize(input
, base::kCodepageUTF8
, &output
))
262 base::ReplaceChars(output
, "/", "_", &output
);
263 if (!output
.empty() && output
.find_first_not_of('.', 0) == std::string::npos
)
268 void PrepareWritableFileAndRun(Profile
* profile
,
269 const base::FilePath
& path
,
270 const PrepareWritableFileCallback
& callback
) {
271 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
272 DCHECK(!callback
.is_null());
274 FileSystemInterface
* file_system
= GetFileSystemByProfile(profile
);
275 if (!file_system
|| !IsUnderDriveMountPoint(path
)) {
276 content::BrowserThread::GetBlockingPool()->PostTask(
277 FROM_HERE
, base::Bind(callback
, FILE_ERROR_FAILED
, base::FilePath()));
281 WriteOnCacheFile(file_system
,
282 ExtractDrivePath(path
),
283 std::string(), // mime_type
287 void EnsureDirectoryExists(Profile
* profile
,
288 const base::FilePath
& directory
,
289 const FileOperationCallback
& callback
) {
290 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
291 DCHECK(!callback
.is_null());
292 if (IsUnderDriveMountPoint(directory
)) {
293 FileSystemInterface
* file_system
= GetFileSystemByProfile(profile
);
295 file_system
->CreateDirectory(
296 ExtractDrivePath(directory
),
297 true /* is_exclusive */,
298 true /* is_recursive */,
301 base::MessageLoopProxy::current()->PostTask(
302 FROM_HERE
, base::Bind(callback
, FILE_ERROR_OK
));
306 void EmptyFileOperationCallback(FileError error
) {
309 bool CreateGDocFile(const base::FilePath
& file_path
,
311 const std::string
& resource_id
) {
312 std::string content
= base::StringPrintf(
313 "{\"url\": \"%s\", \"resource_id\": \"%s\"}",
314 url
.spec().c_str(), resource_id
.c_str());
315 return base::WriteFile(file_path
, content
.data(), content
.size()) ==
316 static_cast<int>(content
.size());
319 GURL
ReadUrlFromGDocFile(const base::FilePath
& file_path
) {
320 return GURL(ReadStringFromGDocFile(file_path
, "url"));
323 std::string
ReadResourceIdFromGDocFile(const base::FilePath
& file_path
) {
324 return ReadStringFromGDocFile(file_path
, "resource_id");
327 bool IsDriveEnabledForProfile(Profile
* profile
) {
328 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
330 if (!chromeos::IsProfileAssociatedWithGaiaAccount(profile
))
333 // Disable Drive if preference is set. This can happen with commandline flag
334 // --disable-drive or enterprise policy, or with user settings.
335 if (profile
->GetPrefs()->GetBoolean(prefs::kDisableDrive
))
341 ConnectionStatusType
GetDriveConnectionStatus(Profile
* profile
) {
342 drive::DriveServiceInterface
* const drive_service
=
343 drive::util::GetDriveServiceByProfile(profile
);
346 return DRIVE_DISCONNECTED_NOSERVICE
;
347 if (net::NetworkChangeNotifier::IsOffline())
348 return DRIVE_DISCONNECTED_NONETWORK
;
349 if (!drive_service
->CanSendRequest())
350 return DRIVE_DISCONNECTED_NOTREADY
;
352 const bool is_connection_cellular
=
353 net::NetworkChangeNotifier::IsConnectionCellular(
354 net::NetworkChangeNotifier::GetConnectionType());
355 const bool disable_sync_over_celluar
=
356 profile
->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular
);
358 if (is_connection_cellular
&& disable_sync_over_celluar
)
359 return DRIVE_CONNECTED_METERED
;
360 return DRIVE_CONNECTED
;