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/media_galleries/fileapi/media_path_filter.h" 6 7 #if defined(OS_WIN) 8 #include <windows.h> 9 #endif 10 11 #include <algorithm> 12 #include <string> 13 14 #include "base/strings/string_util.h" 15 #include "net/base/mime_util.h" 16 17 namespace { 18 19 const base::FilePath::CharType* const kExtraSupportedImageExtensions[] = { 20 // RAW picture file types. 21 // Some of which are just image/tiff. 22 FILE_PATH_LITERAL("3fr"), // (Hasselblad) 23 FILE_PATH_LITERAL("arw"), // (Sony) 24 FILE_PATH_LITERAL("dcr"), // (Kodak) 25 FILE_PATH_LITERAL("dng"), // (Adobe, Leica, Ricoh, Samsung) 26 FILE_PATH_LITERAL("erf"), // (Epson) 27 FILE_PATH_LITERAL("k25"), // (Kodak) 28 FILE_PATH_LITERAL("kdc"), // (Kodak) 29 FILE_PATH_LITERAL("mef"), // (Mamiya) 30 FILE_PATH_LITERAL("mos"), // (Leaf) 31 FILE_PATH_LITERAL("nef"), // (Nikon) 32 FILE_PATH_LITERAL("pef"), // (Pentax) 33 FILE_PATH_LITERAL("sr2"), // (Sony) 34 FILE_PATH_LITERAL("srf"), // (Sony) 35 36 // More RAW picture file types. 37 FILE_PATH_LITERAL("cr2"), // (Canon - image/x-canon-cr2) 38 // Note, some .crw files are just TIFFs. 39 FILE_PATH_LITERAL("crw"), // (Canon - image/x-canon-crw) 40 FILE_PATH_LITERAL("mrw"), // (Minolta - image/x-minolta-mrw) 41 FILE_PATH_LITERAL("orf"), // (Olympus - image/x-olympus-orf) 42 FILE_PATH_LITERAL("raf"), // (Fuji) 43 FILE_PATH_LITERAL("rw2"), // (Panasonic - image/x-panasonic-raw) 44 FILE_PATH_LITERAL("x3f"), // (Sigma - image/x-x3f) 45 46 // There exists many file formats all with the .raw extension. For now, only 47 // the following types are supported: 48 // - TIFF files with .raw extension - image/tiff 49 // - Leica / Panasonic RAW files - image/x-panasonic-raw 50 // - Phase One RAW files - image/x-phaseone-raw 51 FILE_PATH_LITERAL("raw"), 52 }; 53 54 const base::FilePath::CharType* const kExtraSupportedVideoExtensions[] = { 55 FILE_PATH_LITERAL("3gp"), 56 FILE_PATH_LITERAL("3gpp"), 57 FILE_PATH_LITERAL("avi"), 58 FILE_PATH_LITERAL("flv"), 59 FILE_PATH_LITERAL("mkv"), 60 FILE_PATH_LITERAL("mov"), 61 FILE_PATH_LITERAL("mpeg"), 62 FILE_PATH_LITERAL("mpeg4"), 63 FILE_PATH_LITERAL("mpegps"), 64 FILE_PATH_LITERAL("mpg"), 65 FILE_PATH_LITERAL("wmv"), 66 }; 67 68 const base::FilePath::CharType* const kExtraSupportedAudioExtensions[] = { 69 // Many of these file types are audio files in the same containers that the 70 // MIME sniffer already detects as video/subtype. 71 FILE_PATH_LITERAL("aac"), // audio/mpeg 72 FILE_PATH_LITERAL("alac"), // video/mp4 73 FILE_PATH_LITERAL("flac"), // audio/x-flac 74 FILE_PATH_LITERAL("m4b"), // video/mp4 75 FILE_PATH_LITERAL("m4p"), // video/mp4 76 FILE_PATH_LITERAL("wma"), // video/x-ms-asf 77 }; 78 79 bool IsUnsupportedExtension(const base::FilePath::StringType& extension) { 80 std::string mime_type; 81 return !net::GetMimeTypeFromExtension(extension, &mime_type) || 82 !net::IsSupportedMimeType(mime_type); 83 } 84 85 std::vector<base::FilePath::StringType> GetMediaExtensionList( 86 const std::string& mime_type) { 87 std::vector<base::FilePath::StringType> extensions; 88 net::GetExtensionsForMimeType(mime_type, &extensions); 89 std::vector<base::FilePath::StringType>::iterator new_end = 90 std::remove_if(extensions.begin(), 91 extensions.end(), 92 &IsUnsupportedExtension); 93 extensions.erase(new_end, extensions.end()); 94 return extensions; 95 } 96 97 } // namespace 98 99 // static 100 bool MediaPathFilter::ShouldSkip(const base::FilePath& path) { 101 const base::FilePath::StringType base_name = path.BaseName().value(); 102 if (base_name.empty()) 103 return false; 104 105 // Dot files (aka hidden files) 106 if (base_name[0] == '.') 107 return true; 108 109 // Mac OS X file. 110 if (base_name == FILE_PATH_LITERAL("__MACOSX")) 111 return true; 112 113 #if defined(OS_WIN) 114 DWORD file_attributes = ::GetFileAttributes(path.value().c_str()); 115 if ((file_attributes != INVALID_FILE_ATTRIBUTES) && 116 ((file_attributes & FILE_ATTRIBUTE_HIDDEN) != 0)) 117 return true; 118 #else 119 // Windows always creates a recycle bin folder in the attached device to store 120 // all the deleted contents. On non-windows operating systems, there is no way 121 // to get the hidden attribute of windows recycle bin folders that are present 122 // on the attached device. Therefore, compare the file path name to the 123 // recycle bin name and exclude those folders. For more details, please refer 124 // to http://support.microsoft.com/kb/171694. 125 const char win_98_recycle_bin_name[] = "RECYCLED"; 126 const char win_xp_recycle_bin_name[] = "RECYCLER"; 127 const char win_vista_recycle_bin_name[] = "$Recycle.bin"; 128 if ((base::strncasecmp(base_name.c_str(), 129 win_98_recycle_bin_name, 130 strlen(win_98_recycle_bin_name)) == 0) || 131 (base::strncasecmp(base_name.c_str(), 132 win_xp_recycle_bin_name, 133 strlen(win_xp_recycle_bin_name)) == 0) || 134 (base::strncasecmp(base_name.c_str(), 135 win_vista_recycle_bin_name, 136 strlen(win_vista_recycle_bin_name)) == 0)) 137 return true; 138 #endif // defined(OS_WIN) 139 return false; 140 } 141 142 MediaPathFilter::MediaPathFilter() 143 : initialized_(false) { 144 sequence_checker_.DetachFromSequence(); 145 } 146 147 MediaPathFilter::~MediaPathFilter() { 148 } 149 150 bool MediaPathFilter::Match(const base::FilePath& path) { 151 return GetType(path) != MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN; 152 } 153 154 MediaGalleryScanFileType MediaPathFilter::GetType(const base::FilePath& path) { 155 EnsureInitialized(); 156 MediaFileExtensionMap::const_iterator it = 157 media_file_extensions_map_.find( 158 base::StringToLowerASCII(path.Extension())); 159 if (it == media_file_extensions_map_.end()) 160 return MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN; 161 return static_cast<MediaGalleryScanFileType>(it->second); 162 } 163 164 void MediaPathFilter::EnsureInitialized() { 165 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 166 if (initialized_) 167 return; 168 169 // This may require I/O when it calls net::GetExtensionsForMimeType(), so 170 // doing this in the ctor and removing |initialized_| would result in a 171 // ThreadRestrictions failure. 172 AddExtensionsToMediaFileExtensionMap(GetMediaExtensionList("image/*"), 173 MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE); 174 AddExtensionsToMediaFileExtensionMap(GetMediaExtensionList("audio/*"), 175 MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO); 176 AddExtensionsToMediaFileExtensionMap(GetMediaExtensionList("video/*"), 177 MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO); 178 AddAdditionalExtensionsToMediaFileExtensionMap( 179 kExtraSupportedImageExtensions, 180 arraysize(kExtraSupportedImageExtensions), 181 MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE); 182 AddAdditionalExtensionsToMediaFileExtensionMap( 183 kExtraSupportedAudioExtensions, 184 arraysize(kExtraSupportedAudioExtensions), 185 MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO); 186 AddAdditionalExtensionsToMediaFileExtensionMap( 187 kExtraSupportedVideoExtensions, 188 arraysize(kExtraSupportedVideoExtensions), 189 MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO); 190 191 initialized_ = true; 192 } 193 194 void MediaPathFilter::AddExtensionsToMediaFileExtensionMap( 195 const MediaFileExtensionList& extensions_list, 196 MediaGalleryScanFileType type) { 197 for (size_t i = 0; i < extensions_list.size(); ++i) 198 AddExtensionToMediaFileExtensionMap(extensions_list[i].c_str(), type); 199 } 200 201 void MediaPathFilter::AddAdditionalExtensionsToMediaFileExtensionMap( 202 const base::FilePath::CharType* const* extensions_list, 203 size_t extensions_list_size, 204 MediaGalleryScanFileType type) { 205 for (size_t i = 0; i < extensions_list_size; ++i) 206 AddExtensionToMediaFileExtensionMap(extensions_list[i], type); 207 } 208 209 void MediaPathFilter::AddExtensionToMediaFileExtensionMap( 210 const base::FilePath::CharType* extension, 211 MediaGalleryScanFileType type) { 212 base::FilePath::StringType extension_with_sep = 213 base::FilePath::kExtensionSeparator + 214 base::FilePath::StringType(extension); 215 media_file_extensions_map_[extension_with_sep] |= type; 216 } 217