1 // Copyright 2013 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/media_galleries/fileapi/picasa_file_util.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/basictypes.h" 11 #include "base/bind_helpers.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/sys_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 17 #include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h" 18 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h" 19 #include "chrome/common/media_galleries/picasa_types.h" 20 #include "content/public/browser/browser_thread.h" 21 #include "webkit/browser/fileapi/file_system_operation_context.h" 22 #include "webkit/browser/fileapi/file_system_url.h" 23 #include "webkit/browser/fileapi/native_file_util.h" 24 #include "webkit/common/fileapi/file_system_util.h" 25 26 using base::FilePath; 27 using fileapi::DirectoryEntry; 28 using fileapi::FileSystemOperationContext; 29 using fileapi::FileSystemURL; 30 31 namespace picasa { 32 33 namespace { 34 35 base::File::Error FindAlbumInfo(const std::string& key, 36 const AlbumMap* map, 37 AlbumInfo* album_info) { 38 if (!map) 39 return base::File::FILE_ERROR_FAILED; 40 41 AlbumMap::const_iterator it = map->find(key); 42 43 if (it == map->end()) 44 return base::File::FILE_ERROR_NOT_FOUND; 45 46 if (album_info != NULL) 47 *album_info = it->second; 48 49 return base::File::FILE_OK; 50 } 51 52 std::vector<std::string> GetVirtualPathComponents( 53 const fileapi::FileSystemURL& url) { 54 ImportedMediaGalleryRegistry* imported_registry = 55 ImportedMediaGalleryRegistry::GetInstance(); 56 base::FilePath root = imported_registry->ImportedRoot().AppendASCII("picasa"); 57 58 DCHECK(root.IsParent(url.path()) || root == url.path()); 59 base::FilePath virtual_path; 60 root.AppendRelativePath(url.path(), &virtual_path); 61 62 std::vector<std::string> result; 63 fileapi::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result); 64 return result; 65 } 66 67 PicasaDataProvider::DataType GetDataTypeForURL( 68 const fileapi::FileSystemURL& url) { 69 std::vector<std::string> components = GetVirtualPathComponents(url); 70 if (components.size() >= 2 && components[0] == kPicasaDirAlbums) 71 return PicasaDataProvider::ALBUMS_IMAGES_DATA; 72 73 return PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA; 74 } 75 76 } // namespace 77 78 const char kPicasaDirAlbums[] = "albums"; 79 const char kPicasaDirFolders[] = "folders"; 80 81 PicasaFileUtil::PicasaFileUtil(MediaPathFilter* media_path_filter) 82 : NativeMediaFileUtil(media_path_filter), 83 weak_factory_(this) { 84 } 85 86 PicasaFileUtil::~PicasaFileUtil() {} 87 88 void PicasaFileUtil::GetFileInfoOnTaskRunnerThread( 89 scoped_ptr<fileapi::FileSystemOperationContext> context, 90 const fileapi::FileSystemURL& url, 91 const GetFileInfoCallback& callback) { 92 PicasaDataProvider* data_provider = GetDataProvider(); 93 // |data_provider| may be NULL if the file system was revoked before this 94 // operation had a chance to run. 95 if (!data_provider) { 96 GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false); 97 } else { 98 data_provider->RefreshData( 99 GetDataTypeForURL(url), 100 base::Bind(&PicasaFileUtil::GetFileInfoWithFreshDataProvider, 101 weak_factory_.GetWeakPtr(), 102 base::Passed(&context), 103 url, 104 callback)); 105 } 106 } 107 108 void PicasaFileUtil::ReadDirectoryOnTaskRunnerThread( 109 scoped_ptr<fileapi::FileSystemOperationContext> context, 110 const fileapi::FileSystemURL& url, 111 const ReadDirectoryCallback& callback) { 112 PicasaDataProvider* data_provider = GetDataProvider(); 113 // |data_provider| may be NULL if the file system was revoked before this 114 // operation had a chance to run. 115 if (!data_provider) { 116 ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false); 117 } else { 118 data_provider->RefreshData( 119 GetDataTypeForURL(url), 120 base::Bind(&PicasaFileUtil::ReadDirectoryWithFreshDataProvider, 121 weak_factory_.GetWeakPtr(), 122 base::Passed(&context), 123 url, 124 callback)); 125 } 126 } 127 128 base::File::Error PicasaFileUtil::GetFileInfoSync( 129 FileSystemOperationContext* context, const FileSystemURL& url, 130 base::File::Info* file_info, base::FilePath* platform_path) { 131 DCHECK(context); 132 DCHECK(file_info); 133 134 if (platform_path) 135 *platform_path = base::FilePath(); 136 137 std::vector<std::string> components = GetVirtualPathComponents(url); 138 139 switch (components.size()) { 140 case 0: 141 // Root directory. 142 file_info->is_directory = true; 143 return base::File::FILE_OK; 144 case 1: 145 if (components[0] == kPicasaDirAlbums || 146 components[0] == kPicasaDirFolders) { 147 file_info->is_directory = true; 148 return base::File::FILE_OK; 149 } 150 151 break; 152 case 2: 153 if (components[0] == kPicasaDirAlbums) { 154 scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetAlbums(); 155 base::File::Error error = 156 FindAlbumInfo(components[1], album_map.get(), NULL); 157 if (error != base::File::FILE_OK) 158 return error; 159 160 file_info->is_directory = true; 161 return base::File::FILE_OK; 162 } 163 164 if (components[0] == kPicasaDirFolders) { 165 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info, 166 platform_path); 167 } 168 break; 169 case 3: 170 // NativeMediaFileUtil::GetInfo calls into virtual function 171 // PicasaFileUtil::GetLocalFilePath, and that will handle both 172 // album contents and folder contents. 173 base::File::Error result = NativeMediaFileUtil::GetFileInfoSync( 174 context, url, file_info, platform_path); 175 176 DCHECK(components[0] == kPicasaDirAlbums || 177 components[0] == kPicasaDirFolders || 178 result == base::File::FILE_ERROR_NOT_FOUND); 179 180 return result; 181 } 182 183 return base::File::FILE_ERROR_NOT_FOUND; 184 } 185 186 base::File::Error PicasaFileUtil::ReadDirectorySync( 187 fileapi::FileSystemOperationContext* context, 188 const fileapi::FileSystemURL& url, 189 EntryList* file_list) { 190 DCHECK(context); 191 DCHECK(file_list); 192 DCHECK(file_list->empty()); 193 194 base::File::Info file_info; 195 base::FilePath platform_directory_path; 196 base::File::Error error = GetFileInfoSync( 197 context, url, &file_info, &platform_directory_path); 198 199 if (error != base::File::FILE_OK) 200 return error; 201 202 if (!file_info.is_directory) 203 return base::File::FILE_ERROR_NOT_A_DIRECTORY; 204 205 std::vector<std::string> components = GetVirtualPathComponents(url); 206 switch (components.size()) { 207 case 0: { 208 // Root directory. 209 file_list->push_back( 210 DirectoryEntry(kPicasaDirAlbums, DirectoryEntry::DIRECTORY, 0, 211 base::Time())); 212 file_list->push_back( 213 DirectoryEntry(kPicasaDirFolders, DirectoryEntry::DIRECTORY, 0, 214 base::Time())); 215 break; 216 } 217 case 1: 218 if (components[0] == kPicasaDirAlbums) { 219 scoped_ptr<AlbumMap> albums = GetDataProvider()->GetAlbums(); 220 if (!albums) 221 return base::File::FILE_ERROR_NOT_FOUND; 222 223 for (AlbumMap::const_iterator it = albums->begin(); 224 it != albums->end(); ++it) { 225 file_list->push_back( 226 DirectoryEntry(it->first, DirectoryEntry::DIRECTORY, 0, 227 it->second.timestamp)); 228 } 229 } else if (components[0] == kPicasaDirFolders) { 230 scoped_ptr<AlbumMap> folders = GetDataProvider()->GetFolders(); 231 if (!folders) 232 return base::File::FILE_ERROR_NOT_FOUND; 233 234 for (AlbumMap::const_iterator it = folders->begin(); 235 it != folders->end(); ++it) { 236 file_list->push_back( 237 DirectoryEntry(it->first, DirectoryEntry::DIRECTORY, 0, 238 it->second.timestamp)); 239 } 240 } 241 break; 242 case 2: 243 if (components[0] == kPicasaDirAlbums) { 244 scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetAlbums(); 245 AlbumInfo album_info; 246 base::File::Error error = 247 FindAlbumInfo(components[1], album_map.get(), &album_info); 248 if (error != base::File::FILE_OK) 249 return error; 250 251 scoped_ptr<AlbumImages> album_images = 252 GetDataProvider()->FindAlbumImages(album_info.uid, &error); 253 if (error != base::File::FILE_OK) 254 return error; 255 256 for (AlbumImages::const_iterator it = album_images->begin(); 257 it != album_images->end(); 258 ++it) { 259 fileapi::DirectoryEntry entry; 260 base::File::Info info; 261 262 // Simply skip files that we can't get info on. 263 if (fileapi::NativeFileUtil::GetFileInfo(it->second, &info) != 264 base::File::FILE_OK) { 265 continue; 266 } 267 268 file_list->push_back(DirectoryEntry( 269 it->first, DirectoryEntry::FILE, info.size, info.last_modified)); 270 } 271 } 272 273 if (components[0] == kPicasaDirFolders) { 274 EntryList super_list; 275 base::File::Error error = 276 NativeMediaFileUtil::ReadDirectorySync(context, url, &super_list); 277 if (error != base::File::FILE_OK) 278 return error; 279 280 for (EntryList::const_iterator it = super_list.begin(); 281 it != super_list.end(); ++it) { 282 if (!it->is_directory) 283 file_list->push_back(*it); 284 } 285 } 286 287 break; 288 } 289 290 return base::File::FILE_OK; 291 } 292 293 base::File::Error PicasaFileUtil::DeleteDirectorySync( 294 fileapi::FileSystemOperationContext* context, 295 const fileapi::FileSystemURL& url) { 296 return base::File::FILE_ERROR_SECURITY; 297 } 298 299 base::File::Error PicasaFileUtil::DeleteFileSync( 300 fileapi::FileSystemOperationContext* context, 301 const fileapi::FileSystemURL& url) { 302 return base::File::FILE_ERROR_SECURITY; 303 } 304 305 base::File::Error PicasaFileUtil::GetLocalFilePath( 306 FileSystemOperationContext* context, const FileSystemURL& url, 307 base::FilePath* local_file_path) { 308 DCHECK(local_file_path); 309 DCHECK(url.is_valid()); 310 std::vector<std::string> components = GetVirtualPathComponents(url); 311 312 switch (components.size()) { 313 case 2: 314 if (components[0] == kPicasaDirFolders) { 315 scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetFolders(); 316 AlbumInfo album_info; 317 base::File::Error error = 318 FindAlbumInfo(components[1], album_map.get(), &album_info); 319 if (error != base::File::FILE_OK) 320 return error; 321 322 *local_file_path = album_info.path; 323 return base::File::FILE_OK; 324 } 325 break; 326 case 3: 327 if (components[0] == kPicasaDirAlbums) { 328 scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetAlbums(); 329 AlbumInfo album_info; 330 base::File::Error error = 331 FindAlbumInfo(components[1], album_map.get(), &album_info); 332 if (error != base::File::FILE_OK) 333 return error; 334 335 scoped_ptr<AlbumImages> album_images = 336 GetDataProvider()->FindAlbumImages(album_info.uid, &error); 337 if (error != base::File::FILE_OK) 338 return error; 339 340 AlbumImages::const_iterator it = album_images->find(components[2]); 341 if (it == album_images->end()) 342 return base::File::FILE_ERROR_NOT_FOUND; 343 344 *local_file_path = it->second; 345 return base::File::FILE_OK; 346 } 347 348 if (components[0] == kPicasaDirFolders) { 349 scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetFolders(); 350 AlbumInfo album_info; 351 base::File::Error error = 352 FindAlbumInfo(components[1], album_map.get(), &album_info); 353 if (error != base::File::FILE_OK) 354 return error; 355 356 // Not part of this class's mandate to check that it actually exists. 357 *local_file_path = album_info.path.Append(url.path().BaseName()); 358 return base::File::FILE_OK; 359 } 360 361 return base::File::FILE_ERROR_NOT_FOUND; 362 break; 363 } 364 365 // All other cases don't have a local path. The valid cases should be 366 // intercepted by GetFileInfo()/CreateFileEnumerator(). Invalid cases 367 // return a NOT_FOUND error. 368 return base::File::FILE_ERROR_NOT_FOUND; 369 } 370 371 void PicasaFileUtil::GetFileInfoWithFreshDataProvider( 372 scoped_ptr<fileapi::FileSystemOperationContext> context, 373 const fileapi::FileSystemURL& url, 374 const GetFileInfoCallback& callback, 375 bool success) { 376 if (!success) { 377 content::BrowserThread::PostTask( 378 content::BrowserThread::IO, 379 FROM_HERE, 380 base::Bind(callback, base::File::FILE_ERROR_IO, base::File::Info())); 381 return; 382 } 383 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread( 384 context.Pass(), url, callback); 385 } 386 387 void PicasaFileUtil::ReadDirectoryWithFreshDataProvider( 388 scoped_ptr<fileapi::FileSystemOperationContext> context, 389 const fileapi::FileSystemURL& url, 390 const ReadDirectoryCallback& callback, 391 bool success) { 392 if (!success) { 393 content::BrowserThread::PostTask( 394 content::BrowserThread::IO, 395 FROM_HERE, 396 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false)); 397 return; 398 } 399 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread( 400 context.Pass(), url, callback); 401 } 402 403 PicasaDataProvider* PicasaFileUtil::GetDataProvider() { 404 return ImportedMediaGalleryRegistry::PicasaDataProvider(); 405 } 406 407 } // namespace picasa 408