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(StringToLowerASCII(path.Extension())); 158 if (it == media_file_extensions_map_.end()) 159 return MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN; 160 return static_cast<MediaGalleryScanFileType>(it->second); 161 } 162 163 void MediaPathFilter::EnsureInitialized() { 164 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 165 if (initialized_) 166 return; 167 168 // This may require I/O when it calls net::GetExtensionsForMimeType(), so 169 // doing this in the ctor and removing |initialized_| would result in a 170 // ThreadRestrictions failure. 171 AddExtensionsToMediaFileExtensionMap(GetMediaExtensionList("image/*"), 172 MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE); 173 AddExtensionsToMediaFileExtensionMap(GetMediaExtensionList("audio/*"), 174 MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO); 175 AddExtensionsToMediaFileExtensionMap(GetMediaExtensionList("video/*"), 176 MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO); 177 AddAdditionalExtensionsToMediaFileExtensionMap( 178 kExtraSupportedImageExtensions, 179 arraysize(kExtraSupportedImageExtensions), 180 MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE); 181 AddAdditionalExtensionsToMediaFileExtensionMap( 182 kExtraSupportedAudioExtensions, 183 arraysize(kExtraSupportedAudioExtensions), 184 MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO); 185 AddAdditionalExtensionsToMediaFileExtensionMap( 186 kExtraSupportedVideoExtensions, 187 arraysize(kExtraSupportedVideoExtensions), 188 MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO); 189 190 initialized_ = true; 191 } 192 193 void MediaPathFilter::AddExtensionsToMediaFileExtensionMap( 194 const MediaFileExtensionList& extensions_list, 195 MediaGalleryScanFileType type) { 196 for (size_t i = 0; i < extensions_list.size(); ++i) 197 AddExtensionToMediaFileExtensionMap(extensions_list[i].c_str(), type); 198 } 199 200 void MediaPathFilter::AddAdditionalExtensionsToMediaFileExtensionMap( 201 const base::FilePath::CharType* const* extensions_list, 202 size_t extensions_list_size, 203 MediaGalleryScanFileType type) { 204 for (size_t i = 0; i < extensions_list_size; ++i) 205 AddExtensionToMediaFileExtensionMap(extensions_list[i], type); 206 } 207 208 void MediaPathFilter::AddExtensionToMediaFileExtensionMap( 209 const base::FilePath::CharType* extension, 210 MediaGalleryScanFileType type) { 211 base::FilePath::StringType extension_with_sep = 212 base::FilePath::kExtensionSeparator + 213 base::FilePath::StringType(extension); 214 media_file_extensions_map_[extension_with_sep] |= type; 215 } 216