diff options
author | vandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-17 21:58:33 +0000 |
---|---|---|
committer | vandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-17 21:58:33 +0000 |
commit | f8c6cc8048819b4921ea99a723205b0ece36f92f (patch) | |
tree | 7f30f28056c1341395c600c68b848fb2e49df3c3 | |
parent | 140e5584aed841d830d3c3d21312602d82fcef4c (diff) | |
download | chromium_src-f8c6cc8048819b4921ea99a723205b0ece36f92f.zip chromium_src-f8c6cc8048819b4921ea99a723205b0ece36f92f.tar.gz chromium_src-f8c6cc8048819b4921ea99a723205b0ece36f92f.tar.bz2 |
Find iTunes library on windows.
BUG=234837
Review URL: https://chromiumcodereview.appspot.com/14611014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@200897 0039d316-1c4b-4281-b951-d872f2087c98
5 files changed, 345 insertions, 4 deletions
diff --git a/chrome/browser/media_galleries/fileapi/itunes_finder_win.cc b/chrome/browser/media_galleries/fileapi/itunes_finder_win.cc index 9bd8210..68492f6 100644 --- a/chrome/browser/media_galleries/fileapi/itunes_finder_win.cc +++ b/chrome/browser/media_galleries/fileapi/itunes_finder_win.cc @@ -4,10 +4,155 @@ #include "chrome/browser/media_galleries/fileapi/itunes_finder_win.h" +#include <string> + +#include "base/base64.h" +#include "base/base_paths_win.h" +#include "base/file_util.h" +#include "base/files/file_path.h" #include "base/logging.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/common/chrome_paths.h" +#include "third_party/libxml/chromium/libxml_utils.h" namespace itunes { +namespace { + +// Traverse |reader| looking for a node named |name| at the current depth +// of |reader|. +bool SeekToNodeAtCurrentDepth(XmlReader* reader, const std::string& name) { + int depth = reader->Depth(); + do { + if (!reader->SkipToElement()) { + // SkipToElement returns false if the current node is an end element, + // try to advance to the next element and then try again. + if (!reader->Read() || !reader->SkipToElement()) + return false; + } + DCHECK_EQ(depth, reader->Depth()); + if (reader->NodeName() == name) + return true; + } while (reader->Next()); + + return false; +} + +// Search within the dict for |key|. +bool SeekInDict(XmlReader* reader, const std::string& key) { + DCHECK_EQ("dict", reader->NodeName()); + + int dict_content_depth = reader->Depth() + 1; + // Advance past the dict node and into the body of the dictionary. + if (!reader->Read()) + return false; + + while (reader->Depth() >= dict_content_depth) { + if (!SeekToNodeAtCurrentDepth(reader, "key")) + return false; + std::string found_key; + if (!reader->ReadElementContent(&found_key)) + return false; + DCHECK_EQ(dict_content_depth, reader->Depth()); + if (found_key == key) + return true; + } + return false; +} + +// Read the iTunes preferences from |pref_file| and then try to extract the +// library XML location from the XML file. Return it if found. The minimal +// valid snippet of XML is: +// +// <plist> +// <dict> +// <key>User Preferences</key> +// <dict> +// <key>iTunes Library XML Location:1</key> +// <data>Base64 encoded w string path</data> +// </dict> +// </dict> +// </plist> +// +base::FilePath::StringType FindLibraryLocationInPrefXML( + const std::string& pref_file) { + XmlReader reader; + base::FilePath::StringType result; + + if (!reader.LoadFile(pref_file)) + return result; + + // Find the plist node and then search within that tag. + if (!SeekToNodeAtCurrentDepth(&reader, "plist")) + return result; + if (!reader.Read()) + return result; + + if (!SeekToNodeAtCurrentDepth(&reader, "dict")) + return result; + + if (!SeekInDict(&reader, "User Preferences")) + return result; + + if (!SeekToNodeAtCurrentDepth(&reader, "dict")) + return result; + + if (!SeekInDict(&reader, "iTunes Library XML Location:1")) + return result; + + if (!SeekToNodeAtCurrentDepth(&reader, "data")) + return result; + + std::string pref_value; + if (!reader.ReadElementContent(&pref_value)) + return result; + // The data is a base64 encoded wchar_t*. Because Base64Decode uses + // std::strings, the result has to be casted to a wchar_t*. + std::string data; + if (!base::Base64Decode(CollapseWhitespaceASCII(pref_value, true), &data)) + return result; + return base::FilePath::StringType( + reinterpret_cast<const wchar_t*>(data.data()), data.size()/2); +} + +// Try to find the iTunes library XML by examining the iTunes preferences +// file and return it if found. +base::FilePath TryPreferencesFile() { + base::FilePath appdata_dir; + if (!PathService::Get(base::DIR_APP_DATA, &appdata_dir)) + return base::FilePath(); + base::FilePath pref_file = appdata_dir.AppendASCII("Apple Computer") + .AppendASCII("iTunes") + .AppendASCII("iTunesPrefs.xml"); + if (!file_util::PathExists(pref_file)) + return base::FilePath(); + + base::FilePath::StringType library_location = + FindLibraryLocationInPrefXML(pref_file.AsUTF8Unsafe()); + base::FilePath library_file(library_location); + if (!file_util::PathExists(library_file)) + return base::FilePath(); + return library_file; +} + +// Check the default location for the iTunes library XML file. Return the +// location if found. +base::FilePath TryDefaultLocation() { + base::FilePath music_dir; + if (!PathService::Get(chrome::DIR_USER_MUSIC, &music_dir)) + return base::FilePath(); + base::FilePath library_file = + music_dir.AppendASCII("iTunes").AppendASCII("iTunes Music Library.xml"); + + if (!file_util::PathExists(library_file)) + return base::FilePath(); + return library_file; +} + +} // namespace + ITunesFinderWin::ITunesFinderWin(const ITunesFinderCallback& callback) : ITunesFinder(callback) { } @@ -15,8 +160,17 @@ ITunesFinderWin::ITunesFinderWin(const ITunesFinderCallback& callback) ITunesFinderWin::~ITunesFinderWin() {} void ITunesFinderWin::FindITunesLibraryOnFileThread() { - PostResultToUIThread(std::string()); - NOTIMPLEMENTED(); + base::FilePath library_file = TryPreferencesFile(); + + if (library_file.empty()) + library_file = TryDefaultLocation(); + + if (library_file.empty()) { + PostResultToUIThread(std::string()); + return; + } + + PostResultToUIThread(library_file.AsUTF8Unsafe()); } } // namespace itunes diff --git a/chrome/browser/media_galleries/fileapi/itunes_finder_win_unittest.cc b/chrome/browser/media_galleries/fileapi/itunes_finder_win_unittest.cc new file mode 100644 index 0000000..ed3fb39 --- /dev/null +++ b/chrome/browser/media_galleries/fileapi/itunes_finder_win_unittest.cc @@ -0,0 +1,181 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base64.h" +#include "base/base_paths_win.h" +#include "base/bind.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/stringprintf.h" +#include "base/test/scoped_path_override.h" +#include "chrome/browser/media_galleries/fileapi/itunes_finder.h" +#include "chrome/browser/media_galleries/fileapi/itunes_finder_win.h" +#include "chrome/common/chrome_paths.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace itunes { + +namespace { + +std::string EncodePath(const base::FilePath& path) { + std::string input(reinterpret_cast<const char*>(path.value().data()), + path.value().size()*2); + std::string result; + base::Base64Encode(input, &result); + return result; +} + +void TouchFile(const base::FilePath& file) { + ASSERT_EQ(1, file_util::WriteFile(file, " ", 1)); +} + +class ITunesFinderWinTest : public testing::Test { + public: + ITunesFinderWinTest() + : ui_thread_(content::BrowserThread::UI, &loop_), + file_thread_(content::BrowserThread::FILE, &loop_) { + } + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(app_data_dir_.CreateUniqueTempDir()); + ASSERT_TRUE(music_dir_.CreateUniqueTempDir()); + app_data_dir_override_.reset( + new base::ScopedPathOverride(base::DIR_APP_DATA, app_data_dir())); + music_dir_override_.reset( + new base::ScopedPathOverride(chrome::DIR_USER_MUSIC, music_dir())); + } + + const base::FilePath& app_data_dir() { + return app_data_dir_.path(); + } + + const base::FilePath& music_dir() { + return music_dir_.path(); + } + + void WritePrefFile(const std::string& data) { + base::FilePath pref_dir = + app_data_dir().AppendASCII("Apple Computer").AppendASCII("iTunes"); + ASSERT_TRUE(file_util::CreateDirectory(pref_dir)); + ASSERT_EQ(data.size(), + file_util::WriteFile(pref_dir.AppendASCII("iTunesPrefs.xml"), + data.data(), data.size())); + } + + void TouchDefault() { + base::FilePath default_dir = music_dir().AppendASCII("iTunes"); + ASSERT_TRUE(file_util::CreateDirectory(default_dir)); + TouchFile(default_dir.AppendASCII("iTunes Music Library.xml")); + } + + std::string TestFindITunesLibrary() { + std::string result; + ITunesFinder::FindITunesLibrary( + base::Bind(&ITunesFinderWinTest::FinderCallback, + base::Unretained(this), base::Unretained(&result))); + base::RunLoop().RunUntilIdle(); + return result; + } + + private: + void FinderCallback(std::string* result_holder, const std::string& result) { + *result_holder = result; + } + + scoped_ptr<base::ScopedPathOverride> app_data_dir_override_; + scoped_ptr<base::ScopedPathOverride> music_dir_override_; + base::ScopedTempDir app_data_dir_; + base::ScopedTempDir music_dir_; + + MessageLoop loop_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; + + DISALLOW_COPY_AND_ASSIGN(ITunesFinderWinTest); +}; + +TEST_F(ITunesFinderWinTest, NotFound) { + std::string result = TestFindITunesLibrary(); + EXPECT_TRUE(result.empty()); +} + +TEST_F(ITunesFinderWinTest, DefaultLocation) { + TouchDefault(); + std::string result = TestFindITunesLibrary(); + EXPECT_FALSE(result.empty()); +} + +TEST_F(ITunesFinderWinTest, CustomLocation) { + base::FilePath library_xml = music_dir().AppendASCII("library.xml"); + TouchFile(library_xml); + std::string xml = base::StringPrintf( + "<plist>" + " <dict>" + " <key>User Preferences</key>" + " <dict>" + " <key>iTunes Library XML Location:1</key>" + " <data>%s</data>" + " </dict>" + " </dict>" + "</plist>", EncodePath(library_xml).c_str()); + WritePrefFile(xml); + std::string result = TestFindITunesLibrary(); + EXPECT_FALSE(result.empty()); +} + +TEST_F(ITunesFinderWinTest, BadCustomLocation) { + // Missing file. + base::FilePath library_xml = music_dir().AppendASCII("library.xml"); + std::string xml = base::StringPrintf( + "<plist>" + " <dict>" + " <key>User Preferences</key>" + " <dict>" + " <key>iTunes Library XML Location:1</key>" + " <data>%s</data>" + " </dict>" + " </dict>" + "</plist>", EncodePath(library_xml).c_str()); + WritePrefFile(xml); + std::string result = TestFindITunesLibrary(); + EXPECT_TRUE(result.empty()); + TouchFile(library_xml); + + // User Preferences dictionary at the wrong level. + xml = base::StringPrintf( + "<plist>" + " <key>User Preferences</key>" + " <dict>" + " <key>iTunes Library XML Location:1</key>" + " <data>%s</data>" + " </dict>" + "</plist>", EncodePath(library_xml).c_str()); + WritePrefFile(xml); + result = TestFindITunesLibrary(); + EXPECT_TRUE(result.empty()); + + // Library location at the wrong scope. + xml = base::StringPrintf( + "<plist>" + " <dict>" + " <key>User Preferences</key>" + " <dict/>" + " <key>iTunes Library XML Location:1</key>" + " <data>%s</data>" + " </dict>" + "</plist>", EncodePath(library_xml).c_str()); + WritePrefFile(xml); + result = TestFindITunesLibrary(); + EXPECT_TRUE(result.empty()); +} + +} // namespace + +} // namespace itunes diff --git a/chrome/browser/storage_monitor/media_storage_util.cc b/chrome/browser/storage_monitor/media_storage_util.cc index 7efde88..7bb2203 100644 --- a/chrome/browser/storage_monitor/media_storage_util.cc +++ b/chrome/browser/storage_monitor/media_storage_util.cc @@ -84,7 +84,8 @@ void FilterAttachedDevicesOnFileThread(MediaStorageUtil::DeviceIdSet* devices) { continue; } - if (type == StorageInfo::FIXED_MASS_STORAGE) { + if (type == StorageInfo::FIXED_MASS_STORAGE || + type == StorageInfo::ITUNES) { if (!file_util::PathExists(base::FilePath::FromUTF8Unsafe(unique_id))) missing_devices.insert(*it); continue; diff --git a/chrome/browser/storage_monitor/storage_info.cc b/chrome/browser/storage_monitor/storage_info.cc index 65d29f8..c28bb79 100644 --- a/chrome/browser/storage_monitor/storage_info.cc +++ b/chrome/browser/storage_monitor/storage_info.cc @@ -107,7 +107,11 @@ bool StorageInfo::IsMediaDevice(const std::string& device_id) { // static bool StorageInfo::IsRemovableDevice(const std::string& device_id) { Type type; - return CrackDeviceId(device_id, &type, NULL) && type != FIXED_MASS_STORAGE; + return CrackDeviceId(device_id, &type, NULL) && + (type == REMOVABLE_MASS_STORAGE_WITH_DCIM || + type == REMOVABLE_MASS_STORAGE_NO_DCIM || + type == MTP_OR_PTP || + type == MAC_IMAGE_CAPTURE); } // static diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 4ea34a2..ace1a9e 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -906,6 +906,7 @@ 'browser/managed_mode/managed_mode_url_filter_unittest.cc', 'browser/managed_mode/managed_user_passphrase_unittest.cc', 'browser/managed_mode/managed_user_service_unittest.cc', + 'browser/media_galleries/fileapi/itunes_finder_win_unittest.cc', 'browser/media_galleries/fileapi/native_media_file_util_unittest.cc', 'browser/media_galleries/fileapi/picasa/picasa_album_table_reader_unittest.cc', 'browser/media_galleries/fileapi/picasa/pmp_column_reader_unittest.cc', |