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. 4 5 #include "chrome/browser/chromeos/drive/file_system_util.h" 6 7 #include <string> 8 #include <vector> 9 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" 43 44 using content::BrowserThread; 45 46 namespace drive { 47 namespace util { 48 49 namespace { 50 51 std::string ReadStringFromGDocFile(const base::FilePath& file_path, 52 const std::string& key) { 53 const int64 kMaxGDocSize = 4096; 54 int64 file_size = 0; 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(); 58 return std::string(); 59 } 60 61 JSONFileValueSerializer reader(file_path); 62 std::string error_message; 63 scoped_ptr<base::Value> root_value(reader.Deserialize(NULL, &error_message)); 64 if (!root_value) { 65 LOG(WARNING) << "Failed to parse " << file_path.value() << " as JSON." 66 << " error = " << error_message; 67 return std::string(); 68 } 69 70 base::DictionaryValue* dictionary_value = NULL; 71 std::string result; 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; 76 return std::string(); 77 } 78 79 return result; 80 } 81 82 // Returns DriveIntegrationService instance, if Drive is enabled. 83 // Otherwise, NULL. 84 DriveIntegrationService* GetIntegrationServiceByProfile(Profile* profile) { 85 DriveIntegrationService* service = 86 DriveIntegrationServiceFactory::FindForProfile(profile); 87 if (!service || !service->IsMounted()) 88 return NULL; 89 return service; 90 } 91 92 } // namespace 93 94 const base::FilePath& GetDriveGrandRootPath() { 95 CR_DEFINE_STATIC_LOCAL(base::FilePath, grand_root_path, 96 (kDriveGrandRootDirName)); 97 return grand_root_path; 98 } 99 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; 104 } 105 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)); 115 } 116 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 user_manager::User* const user = 125 user_manager::UserManager::IsInitialized() 126 ? chromeos::ProfileHelper::Get()->GetUserByProfile( 127 profile->GetOriginalProfile()) 128 : NULL; 129 if (user) 130 id = user->username_hash(); 131 } 132 return GetDriveMountPointPathForUserIdHash(id); 133 } 134 135 FileSystemInterface* GetFileSystemByProfile(Profile* profile) { 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 137 138 DriveIntegrationService* integration_service = 139 GetIntegrationServiceByProfile(profile); 140 return integration_service ? integration_service->file_system() : NULL; 141 } 142 143 FileSystemInterface* GetFileSystemByProfileId(void* profile_id) { 144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 145 146 // |profile_id| needs to be checked with ProfileManager::IsValidProfile 147 // before using it. 148 Profile* profile = reinterpret_cast<Profile*>(profile_id); 149 if (!g_browser_process->profile_manager()->IsValidProfile(profile)) 150 return NULL; 151 return GetFileSystemByProfile(profile); 152 } 153 154 DriveAppRegistry* GetDriveAppRegistryByProfile(Profile* profile) { 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 156 157 DriveIntegrationService* integration_service = 158 GetIntegrationServiceByProfile(profile); 159 return integration_service ? 160 integration_service->drive_app_registry() : 161 NULL; 162 } 163 164 DriveServiceInterface* GetDriveServiceByProfile(Profile* profile) { 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 166 167 DriveIntegrationService* integration_service = 168 GetIntegrationServiceByProfile(profile); 169 return integration_service ? integration_service->drive_service() : NULL; 170 } 171 172 bool IsUnderDriveMountPoint(const base::FilePath& path) { 173 return !ExtractDrivePath(path).empty(); 174 } 175 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(); 187 188 base::FilePath drive_path = GetDriveGrandRootPath(); 189 for (size_t i = 3; i < components.size(); ++i) 190 drive_path = drive_path.Append(components[i]); 191 return drive_path; 192 } 193 194 Profile* ExtractProfileFromPath(const base::FilePath& path) { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 196 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; 206 } 207 } 208 return NULL; 209 } 210 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()); 216 } 217 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); 226 } 227 228 std::string EscapeCacheFileName(const std::string& filename) { 229 // This is based on net/base/escape.cc: net::(anonymous namespace)::Escape 230 std::string escaped; 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); 235 } else { 236 escaped.push_back(c); 237 } 238 } 239 return escaped; 240 } 241 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]); 249 i += 2; 250 } 251 unescaped.push_back(c); 252 } 253 return unescaped; 254 } 255 256 std::string NormalizeFileName(const std::string& input) { 257 DCHECK(base::IsStringUTF8(input)); 258 259 std::string output; 260 if (!base::ConvertToUtf8AndNormalize(input, base::kCodepageUTF8, &output)) 261 output = input; 262 base::ReplaceChars(output, "/", "_", &output); 263 if (!output.empty() && output.find_first_not_of('.', 0) == std::string::npos) 264 output = "_"; 265 return output; 266 } 267 268 void PrepareWritableFileAndRun(Profile* profile, 269 const base::FilePath& path, 270 const PrepareWritableFileCallback& callback) { 271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 272 DCHECK(!callback.is_null()); 273 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())); 278 return; 279 } 280 281 WriteOnCacheFile(file_system, 282 ExtractDrivePath(path), 283 std::string(), // mime_type 284 callback); 285 } 286 287 void EnsureDirectoryExists(Profile* profile, 288 const base::FilePath& directory, 289 const FileOperationCallback& callback) { 290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 291 DCHECK(!callback.is_null()); 292 if (IsUnderDriveMountPoint(directory)) { 293 FileSystemInterface* file_system = GetFileSystemByProfile(profile); 294 DCHECK(file_system); 295 file_system->CreateDirectory( 296 ExtractDrivePath(directory), 297 true /* is_exclusive */, 298 true /* is_recursive */, 299 callback); 300 } else { 301 base::MessageLoopProxy::current()->PostTask( 302 FROM_HERE, base::Bind(callback, FILE_ERROR_OK)); 303 } 304 } 305 306 void EmptyFileOperationCallback(FileError error) { 307 } 308 309 bool CreateGDocFile(const base::FilePath& file_path, 310 const GURL& url, 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()); 317 } 318 319 GURL ReadUrlFromGDocFile(const base::FilePath& file_path) { 320 return GURL(ReadStringFromGDocFile(file_path, "url")); 321 } 322 323 std::string ReadResourceIdFromGDocFile(const base::FilePath& file_path) { 324 return ReadStringFromGDocFile(file_path, "resource_id"); 325 } 326 327 bool IsDriveEnabledForProfile(Profile* profile) { 328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 329 330 if (!chromeos::IsProfileAssociatedWithGaiaAccount(profile)) 331 return false; 332 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)) 336 return false; 337 338 return true; 339 } 340 341 ConnectionStatusType GetDriveConnectionStatus(Profile* profile) { 342 drive::DriveServiceInterface* const drive_service = 343 drive::util::GetDriveServiceByProfile(profile); 344 345 if (!drive_service) 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; 351 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); 357 358 if (is_connection_cellular && disable_sync_over_celluar) 359 return DRIVE_CONNECTED_METERED; 360 return DRIVE_CONNECTED; 361 } 362 363 } // namespace util 364 } // namespace drive 365