Home | History | Annotate | Download | only in fileapi
      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