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/iphoto_file_util.h" 6 7#include <set> 8#include <string> 9#include <vector> 10 11#include "base/bind_helpers.h" 12#include "base/files/file_util.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/strings/utf_string_conversions.h" 15#include "chrome/browser/media_galleries/fileapi/iphoto_data_provider.h" 16#include "chrome/browser/media_galleries/fileapi/media_path_filter.h" 17#include "chrome/browser/media_galleries/imported_media_gallery_registry.h" 18#include "content/public/browser/browser_thread.h" 19#include "storage/browser/fileapi/file_system_operation_context.h" 20#include "storage/browser/fileapi/file_system_url.h" 21#include "storage/browser/fileapi/native_file_util.h" 22#include "storage/common/blob/shareable_file_reference.h" 23#include "storage/common/fileapi/directory_entry.h" 24#include "storage/common/fileapi/file_system_util.h" 25 26using storage::DirectoryEntry; 27 28namespace iphoto { 29 30namespace { 31 32base::File::Error MakeDirectoryFileInfo(base::File::Info* file_info) { 33 base::File::Info result; 34 result.is_directory = true; 35 *file_info = result; 36 return base::File::FILE_OK; 37} 38 39template <typename T> 40bool ContainsElement(const std::vector<T>& collection, const T& key) { 41 typename std::vector<T>::const_iterator it = collection.begin(); 42 while (it != collection.end()) { 43 if (*it == key) 44 return true; 45 it++; 46 } 47 return false; 48} 49 50std::vector<std::string> GetVirtualPathComponents( 51 const storage::FileSystemURL& url) { 52 ImportedMediaGalleryRegistry* imported_registry = 53 ImportedMediaGalleryRegistry::GetInstance(); 54 base::FilePath root = imported_registry->ImportedRoot().AppendASCII("iphoto"); 55 56 DCHECK(root.IsParent(url.path()) || root == url.path()); 57 base::FilePath virtual_path; 58 root.AppendRelativePath(url.path(), &virtual_path); 59 60 std::vector<std::string> result; 61 storage::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result); 62 return result; 63} 64 65} // namespace 66 67const char kIPhotoAlbumsDir[] = "Albums"; 68const char kIPhotoOriginalsDir[] = "Originals"; 69 70IPhotoFileUtil::IPhotoFileUtil(MediaPathFilter* media_path_filter) 71 : NativeMediaFileUtil(media_path_filter), 72 weak_factory_(this), 73 imported_registry_(NULL) { 74} 75 76IPhotoFileUtil::~IPhotoFileUtil() { 77} 78 79void IPhotoFileUtil::GetFileInfoOnTaskRunnerThread( 80 scoped_ptr<storage::FileSystemOperationContext> context, 81 const storage::FileSystemURL& url, 82 const GetFileInfoCallback& callback) { 83 IPhotoDataProvider* data_provider = GetDataProvider(); 84 // |data_provider| may be NULL if the file system was revoked before this 85 // operation had a chance to run. 86 if (!data_provider) { 87 GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false); 88 } else { 89 data_provider->RefreshData( 90 base::Bind(&IPhotoFileUtil::GetFileInfoWithFreshDataProvider, 91 weak_factory_.GetWeakPtr(), base::Passed(&context), url, 92 callback)); 93 } 94} 95 96void IPhotoFileUtil::ReadDirectoryOnTaskRunnerThread( 97 scoped_ptr<storage::FileSystemOperationContext> context, 98 const storage::FileSystemURL& url, 99 const ReadDirectoryCallback& callback) { 100 IPhotoDataProvider* data_provider = GetDataProvider(); 101 // |data_provider| may be NULL if the file system was revoked before this 102 // operation had a chance to run. 103 if (!data_provider) { 104 ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false); 105 } else { 106 data_provider->RefreshData( 107 base::Bind(&IPhotoFileUtil::ReadDirectoryWithFreshDataProvider, 108 weak_factory_.GetWeakPtr(), base::Passed(&context), url, 109 callback)); 110 } 111} 112 113void IPhotoFileUtil::CreateSnapshotFileOnTaskRunnerThread( 114 scoped_ptr<storage::FileSystemOperationContext> context, 115 const storage::FileSystemURL& url, 116 const CreateSnapshotFileCallback& callback) { 117 IPhotoDataProvider* data_provider = GetDataProvider(); 118 // |data_provider| may be NULL if the file system was revoked before this 119 // operation had a chance to run. 120 if (!data_provider) { 121 CreateSnapshotFileWithFreshDataProvider(context.Pass(), url, callback, 122 false); 123 } else { 124 data_provider->RefreshData( 125 base::Bind(&IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider, 126 weak_factory_.GetWeakPtr(), base::Passed(&context), url, 127 callback)); 128 } 129} 130 131void IPhotoFileUtil::GetFileInfoWithFreshDataProvider( 132 scoped_ptr<storage::FileSystemOperationContext> context, 133 const storage::FileSystemURL& url, 134 const GetFileInfoCallback& callback, 135 bool valid_parse) { 136 if (!valid_parse) { 137 if (!callback.is_null()) { 138 content::BrowserThread::PostTask( 139 content::BrowserThread::IO, 140 FROM_HERE, 141 base::Bind(callback, base::File::FILE_ERROR_IO, base::File::Info())); 142 } 143 return; 144 } 145 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url, 146 callback); 147} 148 149void IPhotoFileUtil::ReadDirectoryWithFreshDataProvider( 150 scoped_ptr<storage::FileSystemOperationContext> context, 151 const storage::FileSystemURL& url, 152 const ReadDirectoryCallback& callback, 153 bool valid_parse) { 154 if (!valid_parse) { 155 if (!callback.is_null()) { 156 content::BrowserThread::PostTask( 157 content::BrowserThread::IO, 158 FROM_HERE, 159 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false)); 160 } 161 return; 162 } 163 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url, 164 callback); 165} 166 167void IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider( 168 scoped_ptr<storage::FileSystemOperationContext> context, 169 const storage::FileSystemURL& url, 170 const CreateSnapshotFileCallback& callback, 171 bool valid_parse) { 172 if (!valid_parse) { 173 if (!callback.is_null()) { 174 base::File::Info file_info; 175 base::FilePath platform_path; 176 scoped_refptr<storage::ShareableFileReference> file_ref; 177 content::BrowserThread::PostTask( 178 content::BrowserThread::IO, 179 FROM_HERE, 180 base::Bind(callback, base::File::FILE_ERROR_IO, file_info, 181 platform_path, file_ref)); 182 } 183 return; 184 } 185 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url, 186 callback); 187} 188 189// Begin actual implementation. 190 191base::File::Error IPhotoFileUtil::GetFileInfoSync( 192 storage::FileSystemOperationContext* context, 193 const storage::FileSystemURL& url, 194 base::File::Info* file_info, 195 base::FilePath* platform_path) { 196 std::vector<std::string> components = GetVirtualPathComponents(url); 197 198 if (components.size() == 0) { 199 return MakeDirectoryFileInfo(file_info); 200 } 201 202 // The 'Albums' directory. 203 if (components[0] == kIPhotoAlbumsDir) { 204 if (components.size() == 1) { 205 return MakeDirectoryFileInfo(file_info); 206 } else if (components.size() == 2) { 207 std::vector<std::string> albums = 208 GetDataProvider()->GetAlbumNames(); 209 if (ContainsElement(albums, components[1])) 210 return MakeDirectoryFileInfo(file_info); 211 } else if (components.size() == 3) { 212 if (components[2] == kIPhotoOriginalsDir) { 213 if (GetDataProvider()->HasOriginals(components[1])) 214 return MakeDirectoryFileInfo(file_info); 215 else 216 return base::File::FILE_ERROR_NOT_FOUND; 217 } 218 219 base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum( 220 components[1], components[2]); 221 if (!location.empty()) { 222 return NativeMediaFileUtil::GetFileInfoSync( 223 context, url, file_info, platform_path); 224 } 225 } else if (components.size() == 4 && 226 GetDataProvider()->HasOriginals(components[1]) && 227 components[2] == kIPhotoOriginalsDir) { 228 base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation( 229 components[1], components[3]); 230 if (!location.empty()) { 231 return NativeMediaFileUtil::GetFileInfoSync( 232 context, url, file_info, platform_path); 233 } 234 } 235 } 236 237 return base::File::FILE_ERROR_NOT_FOUND; 238} 239 240base::File::Error IPhotoFileUtil::ReadDirectorySync( 241 storage::FileSystemOperationContext* context, 242 const storage::FileSystemURL& url, 243 EntryList* file_list) { 244 DCHECK(file_list->empty()); 245 std::vector<std::string> components = GetVirtualPathComponents(url); 246 247 // Root directory. Child is the /Albums dir. 248 if (components.size() == 0) { 249 file_list->push_back(DirectoryEntry(kIPhotoAlbumsDir, 250 DirectoryEntry::DIRECTORY, 251 0, base::Time())); 252 return base::File::FILE_OK; 253 } 254 255 if (components[0] == kIPhotoAlbumsDir) { 256 if (components.size() == 1) { 257 // Albums dir contains all album names. 258 std::vector<std::string> albums = 259 GetDataProvider()->GetAlbumNames(); 260 for (std::vector<std::string>::const_iterator it = albums.begin(); 261 it != albums.end(); it++) { 262 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY, 263 0, base::Time())); 264 } 265 return base::File::FILE_OK; 266 } else if (components.size() == 2) { 267 std::vector<std::string> albums = 268 GetDataProvider()->GetAlbumNames(); 269 if (!ContainsElement(albums, components[1])) 270 return base::File::FILE_ERROR_NOT_FOUND; 271 272 // Album dirs contain all photos in them. 273 if (GetDataProvider()->HasOriginals(components[1])) { 274 file_list->push_back(DirectoryEntry(kIPhotoOriginalsDir, 275 DirectoryEntry::DIRECTORY, 276 0, base::Time())); 277 } 278 std::map<std::string, base::FilePath> locations = 279 GetDataProvider()->GetAlbumContents(components[1]); 280 for (std::map<std::string, base::FilePath>::const_iterator it = 281 locations.begin(); 282 it != locations.end(); it++) { 283 base::File::Info info; 284 if (!base::GetFileInfo(it->second, &info)) 285 return base::File::FILE_ERROR_IO; 286 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE, 287 info.size, info.last_modified)); 288 } 289 return base::File::FILE_OK; 290 } else if (components.size() == 3 && 291 components[2] == kIPhotoOriginalsDir && 292 GetDataProvider()->HasOriginals(components[1])) { 293 std::map<std::string, base::FilePath> originals = 294 GetDataProvider()->GetOriginals(components[1]); 295 for (std::map<std::string, base::FilePath>::const_iterator it = 296 originals.begin(); 297 it != originals.end(); it++) { 298 base::File::Info info; 299 if (!base::GetFileInfo(it->second, &info)) 300 return base::File::FILE_ERROR_IO; 301 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE, 302 info.size, info.last_modified)); 303 } 304 return base::File::FILE_OK; 305 } 306 } 307 308 return base::File::FILE_ERROR_NOT_FOUND; 309} 310 311base::File::Error IPhotoFileUtil::DeleteDirectorySync( 312 storage::FileSystemOperationContext* context, 313 const storage::FileSystemURL& url) { 314 return base::File::FILE_ERROR_SECURITY; 315} 316 317base::File::Error IPhotoFileUtil::DeleteFileSync( 318 storage::FileSystemOperationContext* context, 319 const storage::FileSystemURL& url) { 320 return base::File::FILE_ERROR_SECURITY; 321} 322 323base::File::Error IPhotoFileUtil::GetLocalFilePath( 324 storage::FileSystemOperationContext* context, 325 const storage::FileSystemURL& url, 326 base::FilePath* local_file_path) { 327 std::vector<std::string> components = GetVirtualPathComponents(url); 328 329 if (components.size() == 3 && components[0] == kIPhotoAlbumsDir) { 330 base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum( 331 components[1], components[2]); 332 if (!location.empty()) { 333 *local_file_path = location; 334 return base::File::FILE_OK; 335 } 336 } 337 338 if (components.size() == 4 && components[0] == kIPhotoAlbumsDir && 339 GetDataProvider()->HasOriginals(components[1]) && 340 components[2] == kIPhotoOriginalsDir) { 341 base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation( 342 components[1], components[3]); 343 if (!location.empty()) { 344 *local_file_path = location; 345 return base::File::FILE_OK; 346 } 347 } 348 349 return base::File::FILE_ERROR_NOT_FOUND; 350} 351 352IPhotoDataProvider* IPhotoFileUtil::GetDataProvider() { 353 if (!imported_registry_) 354 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance(); 355 return imported_registry_->IPhotoDataProvider(); 356} 357 358} // namespace iphoto 359