Remove all the soruce for WMDM devices. Fixes issue 3748

This commit is contained in:
David Sansome 2013-06-30 08:25:02 +00:00
parent f245f7ed82
commit eb20fb5945
17 changed files with 0 additions and 1896 deletions

View File

@ -83,7 +83,6 @@ pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
if (WIN32)
find_package(ZLIB REQUIRED)
find_library(MSWMDM_LIBRARIES mswmdm)
find_library(QTSPARKLE_LIBRARIES qtsparkle)
endif (WIN32)

View File

@ -9,9 +9,6 @@
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Clementine"
!define WMDM_DIST_URL "http://clementine-player.googlecode.com/files/wmdmdist.exe"
!define WMF_DIST_URL "http://clementine-player.googlecode.com/files/wmfdist.exe"
; Set Application Capabilities info
!define CAPABILITIES_NAME "Clementine"
!define CAPABILITIES_LOCAL_NAME "Clementine"
@ -911,41 +908,6 @@ Section "Uninstaller"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd
Section "Windows Media Device Manager"
GetDllVersion "$SYSDIR\mswmdm.dll" $R8 $R9
IntOp $R7 $R8 / 0x00010000
IntCmp $R7 9 end downloadwmf end # Major version >= 9
downloadwmf:
StrCpy $1 "wmfdist.exe"
NSISdl::download ${WMF_DIST_URL} "$TEMP\$1"
Pop $R0
StrCmp $R0 "success" downloadwmdm
MessageBox MB_OK|MB_ICONSTOP "Error while downloading $1."
Quit
downloadwmdm:
StrCpy $1 "wmdmdist.exe"
NSISdl::download ${WMDM_DIST_URL} "$TEMP\$1"
Pop $R0
StrCmp $R0 "success" install
MessageBox MB_OK|MB_ICONSTOP "Error while downloading $1."
Quit
install:
DetailPrint "Installing the Windows Media Format 9 redistributable"
ExecWait "$TEMP\wmfdist.exe /Q"
DetailPrint "Installing the Windows Media Device Manager 9 redistributable"
ExecWait "$TEMP\wmdmdist.exe /Q"
Delete "$TEMP\wmfdist.exe"
Delete "$TEMP\wmdmdist.exe"
end:
DetailPrint "WMDM redistributable installed"
SectionEnd
Section "Uninstall"
; Kill clementine.exe if it's running
; This calling convention is retarded...

View File

@ -1045,20 +1045,6 @@ optional_source(HAVE_LIBMTP
devices/mtploader.h
)
# Windows media lister
optional_source(WIN32
SOURCES
devices/wmdmdevice.cpp
devices/wmdmlister.cpp
devices/wmdmloader.cpp
devices/wmdmprogress.cpp
devices/wmdmthread.cpp
HEADERS
devices/wmdmdevice.h
devices/wmdmlister.h
devices/wmdmloader.h
)
# Moodbar support
optional_source(HAVE_MOODBAR
SOURCES
@ -1297,7 +1283,6 @@ endif(HAVE_STATIC_SQLITE)
if (WIN32)
target_link_libraries(clementine_lib
${ZLIB_LIBRARIES}
${MSWMDM_LIBRARIES}
${QTSPARKLE_LIBRARIES}
tinysvcmdns
qtwin

View File

@ -40,11 +40,6 @@
#include <id3v1genres.h>
#ifdef Q_OS_WIN32
# include <mswmdm.h>
# include <QUuid>
#endif // Q_OS_WIN32
#ifdef HAVE_LIBGPOD
# include <gpod/itdb.h>
#endif
@ -726,233 +721,6 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
}
#endif
#if defined(Q_OS_WIN32)
static void AddWmdmItem(IWMDMMetaData* metadata, const wchar_t* name,
const QVariant& value) {
switch (value.type()) {
case QVariant::Int:
case QVariant::UInt: {
DWORD data = value.toUInt();
metadata->AddItem(WMDM_TYPE_DWORD, name, (BYTE*)&data, sizeof(data));
break;
}
case QVariant::String: {
ScopedWCharArray data(value.toString());
metadata->AddItem(WMDM_TYPE_STRING, name, (BYTE*)data.get(), data.bytes());
break;
}
case QVariant::ByteArray: {
QByteArray data = value.toByteArray();
metadata->AddItem(WMDM_TYPE_BINARY, name, (BYTE*)data.constData(), data.size());
break;
}
case QVariant::Bool: {
int data = value.toBool();
metadata->AddItem(WMDM_TYPE_BOOL, name, (BYTE*)&data, sizeof(data));
break;
}
case QVariant::LongLong:
case QVariant::ULongLong: {
quint64 data = value.toULongLong();
metadata->AddItem(WMDM_TYPE_QWORD, name, (BYTE*)&data, sizeof(data));
break;
}
default:
qLog(Warning) << "Type" << value.type() << "not handled";
Q_ASSERT(0);
break;
}
}
static QVariant ReadWmdmValue(int type, uchar* data, uint length) {
switch (type) {
case WMDM_TYPE_DWORD:
return QVariant::fromValue(uint(*reinterpret_cast<DWORD*>(data)));
case WMDM_TYPE_WORD:
return QVariant::fromValue(uint(*reinterpret_cast<WORD*>(data)));
case WMDM_TYPE_QWORD:
return QVariant::fromValue(qulonglong(*reinterpret_cast<quint64*>(data)));
case WMDM_TYPE_STRING:
return QString::fromWCharArray(reinterpret_cast<wchar_t*>(data), length/2);
case WMDM_TYPE_BINARY:
return QByteArray(reinterpret_cast<char*>(data), length);
case WMDM_TYPE_BOOL:
return bool(*reinterpret_cast<int*>(data));
case WMDM_TYPE_GUID:
return QUuid(*reinterpret_cast<GUID*>(data)).toString();
}
return QVariant();
}
void Song::InitFromWmdm(IWMDMMetaData* metadata) {
bool non_consumable = false;
int format = 0;
// How much metadata is there?
uint count = 0;
metadata->GetItemCount(&count);
for (int i=0 ; i<count ; ++i) {
// Get this metadata item
wchar_t* name = NULL;
WMDM_TAG_DATATYPE type;
BYTE* value = NULL;
uint length = 0;
metadata->QueryByIndex(i, &name, &type, &value, &length);
QVariant item_value = ReadWmdmValue(type, value, length);
// Store it in the song if it's something we recognise
if (wcscmp(name, g_wszWMDMTitle) == 0)
d->title_ = item_value.toString();
else if (wcscmp(name, g_wszWMDMAuthor) == 0)
d->artist_ = item_value.toString();
else if (wcscmp(name, g_wszWMDMDescription) == 0)
d->comment_ = item_value.toString();
else if (wcscmp(name, g_wszWMDMAlbumTitle) == 0)
d->album_ = item_value.toString();
else if (wcscmp(name, g_wszWMDMTrack) == 0)
d->track_ = item_value.toInt();
else if (wcscmp(name, g_wszWMDMGenre) == 0)
d->genre_ = item_value.toString();
else if (wcscmp(name, g_wszWMDMYear) == 0)
d->year_ = item_value.toInt();
else if (wcscmp(name, g_wszWMDMComposer) == 0)
d->composer_ = item_value.toString();
else if (wcscmp(name, g_wszWMDMBitrate) == 0)
d->bitrate_ = item_value.toInt();
else if (wcscmp(name, g_wszWMDMFileName) == 0)
d->url_ = QUrl::fromLocalFile(item_value.toString());
else if (wcscmp(name, g_wszWMDMDuration) == 0)
set_length_nanosec(item_value.toULongLong() * 1e2);
else if (wcscmp(name, L"WMDM/FileSize") == 0)
d->filesize_ = item_value.toULongLong();
else if (wcscmp(name, L"WMDM/NonConsumable") == 0)
non_consumable = item_value.toBool();
else if (wcscmp(name, L"WMDM/FormatCode") == 0)
format = item_value.toInt();
CoTaskMemFree(name);
CoTaskMemFree(value);
}
// Decide if this is music or not
if (count == 0 || non_consumable)
return;
switch (format) {
case WMDM_FORMATCODE_AIFF:
d->filetype_ = Song::Type_Aiff;
break;
case WMDM_FORMATCODE_WAVE:
d->filetype_ = Song::Type_Wav;
break;
case WMDM_FORMATCODE_MP2:
case WMDM_FORMATCODE_MP3:
case WMDM_FORMATCODE_MPEG:
d->filetype_ = Song::Type_Mpeg;
break;
case WMDM_FORMATCODE_WMA:
case WMDM_FORMATCODE_ASF:
d->filetype_ = Song::Type_Asf;
break;
case WMDM_FORMATCODE_OGG:
d->filetype_ = Song::Type_OggVorbis;
break;
case WMDM_FORMATCODE_AAC:
case WMDM_FORMATCODE_MP4:
d->filetype_ = Song::Type_Mp4;
break;
case WMDM_FORMATCODE_FLAC:
d->filetype_ = Song::Type_Flac;
break;
case WMDM_FORMATCODE_AUDIBLE:
case WMDM_FORMATCODE_UNDEFINEDAUDIO:
d->filetype_ = Song::Type_Unknown;
break;
case WMDM_FORMATCODE_UNDEFINED:
// WMDM doesn't know what type of file it is, so we start guessing - first
// check if any of the music metadata fields were defined. If they were,
// there's a fairly good chance the file was music.
if (!d->title_.isEmpty() || !d->artist_.isEmpty() ||
!d->album_.isEmpty() || !d->comment_.isEmpty() ||
!d->genre_.isEmpty() || d->track_ != -1 || d->year_ != -1 ||
length_nanosec() != -1) {
d->filetype_ = Song::Type_Unknown;
break;
}
// Make a final guess based on the file extension
{
QString ext = d->url_.path().section('.', -1, -1).toLower();
if (ext == "mp3" || ext == "wma" || ext == "flac" || ext == "ogg" ||
ext == "spx" || ext == "mp4" || ext == "aac" || ext == "m4a")
break;
}
return;
default:
return; // It's not music
}
d->valid_ = true;
d->mtime_ = 0;
d->ctime_ = 0;
}
void Song::ToWmdm(IWMDMMetaData* metadata) const {
AddWmdmItem(metadata, g_wszWMDMTitle, d->title_);
AddWmdmItem(metadata, g_wszWMDMAuthor, d->artist_);
AddWmdmItem(metadata, g_wszWMDMDescription, d->comment_);
AddWmdmItem(metadata, g_wszWMDMAlbumTitle, d->album_);
AddWmdmItem(metadata, g_wszWMDMTrack, d->track_);
AddWmdmItem(metadata, g_wszWMDMGenre, d->genre_);
AddWmdmItem(metadata, g_wszWMDMYear, QString::number(d->year_));
AddWmdmItem(metadata, g_wszWMDMComposer, d->composer_);
AddWmdmItem(metadata, g_wszWMDMBitrate, d->bitrate_);
AddWmdmItem(metadata, g_wszWMDMFileName, d->basefilename_);
AddWmdmItem(metadata, g_wszWMDMDuration, qint64(length_nanosec() / 1e2));
AddWmdmItem(metadata, L"WMDM/FileSize", d->filesize_);
WMDM_FORMATCODE format;
switch (d->filetype_) {
case Type_Aiff: format = WMDM_FORMATCODE_AIFF; break;
case Type_Wav: format = WMDM_FORMATCODE_WAVE; break;
case Type_Mpeg: format = WMDM_FORMATCODE_MP3; break;
case Type_Asf: format = WMDM_FORMATCODE_ASF; break;
case Type_OggFlac:
case Type_OggSpeex:
case Type_OggVorbis: format = WMDM_FORMATCODE_OGG; break;
case Type_Mp4: format = WMDM_FORMATCODE_MP4; break;
case Type_Flac: format = WMDM_FORMATCODE_FLAC; break;
default: format = WMDM_FORMATCODE_UNDEFINEDAUDIO; break;
}
AddWmdmItem(metadata, L"WMDM/FormatCode", format);
}
#endif // Q_OS_WIN32
void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
if (d->init_from_file_ || d->url_.scheme() == "file") {
// This Song was already loaded using taglib. Our tags are probably better

View File

@ -44,10 +44,6 @@ class QUrl;
struct LIBMTP_track_struct;
#endif
#if defined(Q_OS_WIN32)
struct IWMDMMetaData;
#endif
#ifdef HAVE_LIBLASTFM
namespace lastfm {
class Track;
@ -126,11 +122,6 @@ class Song {
void ToMTP(LIBMTP_track_struct* track) const;
#endif
#if defined(Q_OS_WIN32)
void InitFromWmdm(IWMDMMetaData* metadata);
void ToWmdm(IWMDMMetaData* metadata) const;
#endif
// Copies important statistics from the other song to this one, overwriting
// any data that already exists. Useful when you want updated tags from disk
// but you want to keep user stats.

View File

@ -50,10 +50,6 @@
#ifdef Q_OS_DARWIN
# include "macdevicelister.h"
#endif
#ifdef Q_OS_WIN32
# include "wmdmlister.h"
# include "wmdmdevice.h"
#endif
#ifdef HAVE_LIBGPOD
# include "gpoddevice.h"
#endif
@ -202,10 +198,6 @@ DeviceManager::DeviceManager(Application* app, QObject *parent)
#ifdef Q_OS_DARWIN
AddLister(new MacDeviceLister);
#endif
#if defined(Q_OS_WIN32)
AddLister(new WmdmLister);
AddDeviceClass<WmdmDevice>();
#endif
AddDeviceClass<FilesystemDevice>();

View File

@ -1,292 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "devicemanager.h"
#include "wmdmdevice.h"
#include "wmdmlister.h"
#include "wmdmloader.h"
#include "wmdmprogress.h"
#include "wmdmthread.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/utilities.h"
#include "library/librarybackend.h"
#include "library/librarymodel.h"
#include <QDir>
#include <QThread>
#include <boost/scoped_array.hpp>
#include <mswmdm.h>
WmdmDevice::WmdmDevice(const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
Application* app,
int database_id, bool first_time)
: ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time),
loader_thread_(new QThread(this)),
loader_(NULL)
{
}
WmdmDevice::~WmdmDevice() {
}
void WmdmDevice::Init() {
InitBackendDirectory("/", first_time_, false);
model_->Init();
loader_ = new WmdmLoader(app_->task_manager(), backend_, shared_from_this());
loader_->moveToThread(loader_thread_);
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(loader_, SIGNAL(LoadFinished()), SLOT(LoadFinished()));
connect(loader_thread_, SIGNAL(started()), loader_, SLOT(LoadDatabase()));
loader_thread_->start();
db_busy_.lock();
}
void WmdmDevice::LoadFinished() {
loader_->deleteLater();
loader_ = NULL;
db_busy_.unlock();
}
bool WmdmDevice::StartCopy(QList<Song::FileType>* supported_types) {
// Ensure only one "organise files" can be active at any one time
db_busy_.lock();
// This initialises COM and gets a connection to the device
thread_.reset(new WmdmThread);
if (!thread_->manager())
return false;
// Find a place to put the files. We default to the root folder for now, but
// could look for a "Music" folder in the future?
WmdmLister* wmdm_lister = static_cast<WmdmLister*>(lister());
QString canonical_name = wmdm_lister->DeviceCanonicalName(unique_id());
IWMDMStorage* destination = thread_->GetRootStorage(canonical_name);
// Get the control interface
destination->QueryInterface(IID_IWMDMStorageControl3, (void**)&storage_control_);
// Get the storage3 interface for CreateEmptyMetadataObject later
destination->QueryInterface(IID_IWMDMStorage3, (void**)&storage_);
destination->Release();
// Did the caller want a list of supported filetypes?
if (supported_types) {
IWMDMDevice* device = thread_->GetDeviceByCanonicalName(canonical_name);
if (!GetSupportedFiletypes(supported_types, device)) {
device->Release();
FinishCopy(false);
return false;
}
device->Release();
}
return true;
}
bool WmdmDevice::CopyToStorage(const CopyJob& job) {
if (!storage_control_ || !storage_)
return false;
// Create the song metadata
IWMDMMetaData* metadata_iface = NULL;
storage_->CreateEmptyMetadataObject(&metadata_iface);
job.metadata_.ToWmdm(metadata_iface);
// Convert the filenames to wchars
ScopedWCharArray source_filename(QDir::toNativeSeparators(job.source_));
ScopedWCharArray dest_filename(job.metadata_.basefilename());
// Create the progress object
WmdmProgress progress(job.progress_);
// Copy the file
IWMDMStorage* new_storage = NULL;
if (storage_control_->Insert3(
WMDM_MODE_BLOCK | WMDM_STORAGECONTROL_INSERTINTO |
WMDM_FILE_CREATE_OVERWRITE | WMDM_CONTENT_FILE,
WMDM_FILE_ATTR_FOLDER,
source_filename,
dest_filename,
NULL, // operation
&progress, // progress
metadata_iface,
NULL, // data
&new_storage)) {
qLog(Warning) << "Couldn't copy file to WMDM device";
metadata_iface->Release();
return false;
}
metadata_iface->Release();
if (!new_storage)
return false;
// Get the metadata from the newly copied file
IWMDMStorage3* new_storage3 = NULL;
IWMDMMetaData* new_metadata = NULL;
new_storage->QueryInterface(IID_IWMDMStorage3, (void**)&new_storage3);
new_storage3->GetMetadata(&new_metadata);
new_storage->Release();
new_storage3->Release();
if (!new_metadata)
return false;
// Add it to our LibraryModel
Song new_song;
new_song.InitFromWmdm(new_metadata);
new_song.set_directory_id(1);
songs_to_add_ << new_song;
new_metadata->Release();
// Remove the original if requested
if (job.remove_original_) {
if (!QFile::remove(job.source_))
return false;
}
return true;
}
void WmdmDevice::FinishCopy(bool success) {
if (success) {
if (!songs_to_add_.isEmpty())
backend_->AddOrUpdateSongs(songs_to_add_);
if (!songs_to_remove_.isEmpty())
backend_->DeleteSongs(songs_to_remove_);
}
songs_to_add_.clear();
songs_to_remove_.clear();
storage_->Release();
storage_control_->Release();
thread_.reset();
db_busy_.unlock();
ConnectedDevice::FinishCopy(success);
}
void WmdmDevice::StartDelete() {
StartCopy(NULL);
}
bool WmdmDevice::DeleteFromStorage(const DeleteJob& job) {
// Walk down the tree until we've found the file
IWMDMStorage3* storage = storage_;
storage->AddRef();
const QStringList path_components =
job.metadata_.url().path().split('/', QString::SkipEmptyParts);
foreach (const QString& path_component, path_components) {
ScopedWCharArray path_component_wchar(path_component);
IWMDMStorage* next_storage = NULL;
if (storage->GetStorage(path_component_wchar, &next_storage)) {
// Couldn't find it
storage->Release();
return false;
}
storage->Release();
next_storage->QueryInterface(IID_IWMDMStorage3, (void**)&storage);
}
// Get a control interface on it
IWMDMStorageControl3* control = NULL;
storage->QueryInterface(IID_IWMDMStorageControl3, (void**)&control);
storage->Release();
// Delete it
WmdmProgress progress;
if (control->Delete(WMDM_MODE_BLOCK, &progress)) {
return false;
}
// Remove it from our library model
songs_to_remove_ << job.metadata_;
return true;
}
void WmdmDevice::FinishDelete(bool success) {
FinishCopy(success);
}
bool WmdmDevice::GetSupportedFiletypes(QList<Song::FileType>* ret, IWMDMDevice* device) {
// Get a list of supported formats
uint32_t format_count = 0;
uint32_t mime_count = 0;
_WAVEFORMATEX* formats;
wchar_t** mime_types;
if (device->GetFormatSupport(
&formats, &format_count, &mime_types, &mime_count)) {
qLog(Warning) << "Unable to get a list of supported formats for device";
return false;
}
// Find known mime types
for (int i=0 ; i<mime_count ; ++i) {
QString type = QString::fromWCharArray(mime_types[i]);
if (type == "audio/mp3" || type == "audio/mpeg")
*ret << Song::Type_Mpeg;
else if (type == "audio/x-ms-wma")
*ret << Song::Type_Asf;
else if (type == "audio/wav")
*ret << Song::Type_Wav;
else if (type == "audio/mp4")
*ret << Song::Type_Mp4;
else if (type == "audio/ogg" || type == "audio/vorbis")
*ret << Song::Type_OggVorbis;
}
CoTaskMemFree(formats);
CoTaskMemFree(mime_types);
return true;
}
bool WmdmDevice::GetSupportedFiletypes(QList<Song::FileType>* ret) {
QMutexLocker l(&db_busy_);
WmdmThread thread;
// Get the device
WmdmLister* wmdm_lister = static_cast<WmdmLister*>(lister());
QString canonical_name = wmdm_lister->DeviceCanonicalName(unique_id());
IWMDMDevice* device = thread.GetDeviceByCanonicalName(canonical_name);
bool success = GetSupportedFiletypes(ret, device);
device->Release();
return success;
}

View File

@ -1,75 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WMDMDEVICE_H
#define WMDMDEVICE_H
#include "connecteddevice.h"
#include <boost/scoped_ptr.hpp>
class WmdmLoader;
class WmdmThread;
struct IWMDMDevice;
struct IWMDMStorage3;
struct IWMDMStorageControl3;
class WmdmDevice : public ConnectedDevice {
Q_OBJECT
public:
Q_INVOKABLE WmdmDevice(const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
Application* app,
int database_id, bool first_time);
~WmdmDevice();
static QStringList url_schemes() { return QStringList() << "wmdm"; }
void Init();
bool GetSupportedFiletypes(QList<Song::FileType>* ret);
bool StartCopy(QList<Song::FileType>* supported_types);
bool CopyToStorage(const CopyJob& job);
void FinishCopy(bool success);
void StartDelete();
bool DeleteFromStorage(const DeleteJob& job);
void FinishDelete(bool success);
private slots:
void LoadFinished();
private:
bool GetSupportedFiletypes(QList<Song::FileType>* ret, IWMDMDevice* device);
private:
QThread* loader_thread_;
WmdmLoader* loader_;
QMutex db_busy_;
SongList songs_to_add_;
SongList songs_to_remove_;
boost::scoped_ptr<WmdmThread> thread_;
IWMDMStorage3* storage_;
IWMDMStorageControl3* storage_control_;
};
#endif // WMDMDEVICE_H

View File

@ -1,528 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#define _WIN32_WINNT 0x0501
#include "wmdmlister.h"
#include "wmdmthread.h"
#include "core/logging.h"
#include "core/utilities.h"
#include <objbase.h>
#include <mswmdm_i.c>
#include <winbase.h>
#include <boost/bind.hpp>
#include <boost/scoped_array.hpp>
#include <QDir>
#include <QPixmap>
#include <QStringList>
#include <QtDebug>
const QUuid WmdmLister::kDeviceProtocolMsc(
0xa4d2c26c, 0xa881, 0x44bb, 0xbd, 0x5d, 0x1f, 0x70, 0x3c, 0x71, 0xf7, 0xa9);
QString WmdmLister::CanonicalNameToId(const QString& canonical_name) {
return "wmdm/" + canonical_name;
}
QString WmdmLister::DeviceInfo::unique_id() const {
return WmdmLister::CanonicalNameToId(canonical_name_);
}
WmdmLister::WmdmLister()
: notification_cookie_(0)
{
}
WmdmLister::~WmdmLister() {
Q_ASSERT(!thread_);
}
void WmdmLister::Init() {
qLog(Debug) << "Starting";
thread_.reset(new WmdmThread);
if (!thread_->manager())
return;
// Register for notifications
qLog(Debug) << "Obtaining CP container";
IConnectionPointContainer* cp_container = NULL;
thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
qLog(Debug) << "Obtaining CP";
IConnectionPoint* cp = NULL;
cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp);
qLog(Debug) << "Registering for notifications";
cp->Advise(this, &notification_cookie_);
cp->Release();
cp_container->Release();
// Fetch the initial list of devices
qLog(Debug) << "Fetching device list";
IWMDMEnumDevice* device_it = NULL;
if (thread_->manager()->EnumDevices2(&device_it)) {
qLog(Warning) << "Error querying WMDM devices";
return;
}
// Iterate through the devices
QMap<QString, DeviceInfo> devices;
forever {
IWMDMDevice* device = NULL;
IWMDMDevice2* device2 = NULL;
ULONG fetched = 0;
if (device_it->Next(1, &device, &fetched) || fetched != 1)
break;
qLog(Debug) << "Querying device";
if (device->QueryInterface(IID_IWMDMDevice2, (void**)&device2)) {
qLog(Warning) << "Error getting IWMDMDevice2 from device";
device->Release();
continue;
}
device->Release();
DeviceInfo info = ReadDeviceInfo(device2);
if (info.is_suitable_)
devices[info.unique_id()] = info;
else
device2->Release();
}
device_it->Release();
// Update the internal cache
qLog(Debug) << "Updating device cache";
{
QMutexLocker l(&mutex_);
devices_ = devices;
}
// Notify about the changes
foreach (const QString& id, devices.keys()) {
emit DeviceAdded(id);
}
qLog(Debug) << "Startup complete";
}
void WmdmLister::ReallyShutdown() {
// Unregister for notifications
IConnectionPointContainer* cp_container;
thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
IConnectionPoint* cp;
cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp);
cp->Release();
cp_container->Release();
thread_.reset();
}
void WmdmLister::ShutDown() {
// COM shutdown must be done in the original thread.
metaObject()->invokeMethod(this, "ReallyShutdown", Qt::BlockingQueuedConnection);
}
WmdmLister::DeviceInfo WmdmLister::ReadDeviceInfo(IWMDMDevice2* device) {
qLog(Debug) << "Reading device info";
DeviceInfo ret;
ret.device_ = device;
// Get text strings
const int max_size = 512;
wchar_t buf[max_size];
device->GetName(buf, max_size);
ret.name_ = QString::fromWCharArray(buf).trimmed();
device->GetManufacturer(buf, max_size);
ret.manufacturer_ = QString::fromWCharArray(buf).trimmed();
device->GetCanonicalName(buf, max_size);
ret.canonical_name_ = QString::fromWCharArray(buf).toLower();
qLog(Debug) << "Read device strings:" << ret.name_ << ret.manufacturer_ << ret.canonical_name_;
// Upgrade to a device3
IWMDMDevice3* device3 = NULL;
device->QueryInterface(IID_IWMDMDevice3, (void**)&device3);
// Get the device protocol so we can figure out whether the device is MSC
PROPVARIANT protocol;
if (device3) {
device3->GetProperty(g_wszWMDMDeviceProtocol, &protocol);
device3->Release();
}
// Get the type and check whether it has storage
DWORD type = 0;
device->GetType(&type);
if (type & WMDM_DEVICE_TYPE_STORAGE)
ret.is_suitable_ = true;
// Get the icon
HICON icon;
if (device->GetDeviceIcon((ULONG*)&icon) == S_OK) {
// Extra check for whether the icon is valid (see issue 1417)
ICONINFO iconinfo;
if (GetIconInfo(icon, &iconinfo)) {
ret.icon_ = QPixmap::fromWinHICON(icon);
DestroyIcon(icon);
}
}
// Get the main (first) storage for the device
IWMDMEnumStorage* storage_it = NULL;
if (device->EnumStorage(&storage_it) == S_OK && storage_it) {
ULONG storage_fetched = 0;
IWMDMStorage* storage;
if (storage_it->Next(1, &storage, &storage_fetched) == S_OK) {
if (storage->QueryInterface(IID_IWMDMStorage2, (void**)&ret.storage_)) {
qLog(Warning) << "Error getting IWMDMStorage2 from storage";
} else {
// Get free space information
UpdateFreeSpace(&ret);
}
storage->Release();
}
storage_it->Release();
}
// There doesn't seem to be a way to get the drive letter of MSC devices, so
// try parsing the device's name to extract it.
if (!device3 || QUuid(*protocol.puuid) == kDeviceProtocolMsc)
GuessDriveLetter(&ret);
return ret;
}
void WmdmLister::GuessDriveLetter(DeviceInfo* info) {
qLog(Debug) << "Guessing drive letter for" << info->name_;
// Windows XP puts the drive letter in brackets at the end of the name
QRegExp drive_letter("\\(([A-Z]:)\\)$");
if (drive_letter.indexIn(info->name_) != -1) {
qLog(Debug) << "Looks like an XP drive" << drive_letter.cap(1);
CheckDriveLetter(info, drive_letter.cap(1));
return;
}
// Windows 7 sometimes has the drive letter as the whole name
drive_letter = QRegExp("^([A-Z]:)\\\\$");
if (drive_letter.indexIn(info->name_) != -1) {
qLog(Debug) << "Looks like a win7 drive" << drive_letter.cap(1);
CheckDriveLetter(info, drive_letter.cap(1));
return;
}
// Otherwise Windows 7 uses the drive's DOS label as its whole name.
// Let's enumerate all the volumes on the system and find one with that
// label, then get its drive letter. Yay!
wchar_t volume_name[MAX_PATH + 1];
HANDLE handle = FindFirstVolumeW(volume_name, MAX_PATH);
forever {
// QueryDosDeviceW doesn't allow a trailing backslash, so remove it.
int length = wcslen(volume_name);
volume_name[length - 1] = L'\0';
wchar_t device_name[MAX_PATH + 1];
QueryDosDeviceW(&volume_name[4], device_name, MAX_PATH);
volume_name[length - 1] = L'\\';
// Don't do cd-roms or floppies
if (QString::fromWCharArray(device_name).contains("HarddiskVolume")) {
wchar_t volume_path[MAX_PATH + 1];
DWORD volume_path_length = MAX_PATH;
GetVolumePathNamesForVolumeNameW(
volume_name, volume_path, volume_path_length, &volume_path_length);
if (wcslen(volume_path) == 3) {
ScopedWCharArray name(QString(MAX_PATH + 1, '\0'));
ScopedWCharArray type(QString(MAX_PATH + 1, '\0'));
DWORD serial = 0;
if (!GetVolumeInformationW(volume_path, name, MAX_PATH,
&serial, NULL, NULL, type, MAX_PATH)) {
qLog(Warning) << "Error getting volume information for" <<
QString::fromWCharArray(volume_path);
} else {
if (name.ToString() == info->name_ && name.characters() != 0) {
// We found it!
qLog(Debug) << "Looks like a win7 drive name" << QString::fromWCharArray(volume_path);
if (CheckDriveLetter(info, QString::fromWCharArray(volume_path))) {
info->device_name_ = QString::fromWCharArray(device_name);
info->volume_name_ = QString::fromWCharArray(volume_name);
}
break;
}
}
}
}
if (!FindNextVolumeW(handle, volume_name, MAX_PATH))
break;
}
FindVolumeClose(handle);
}
bool WmdmLister::CheckDriveLetter(DeviceInfo* info, const QString& drive) {
// Sanity check to make sure there really is a drive there
ScopedWCharArray path(drive.endsWith('\\') ? drive : (drive + "\\"));
ScopedWCharArray name(QString(MAX_PATH + 1, '\0'));
ScopedWCharArray type(QString(MAX_PATH + 1, '\0'));
DWORD serial = 0;
if (!GetVolumeInformationW(
path,
name, MAX_PATH,
&serial,
NULL, // max component length
NULL, // flags
type, MAX_PATH // fat or ntfs
)) {
qLog(Warning) << "Error getting volume information for" << drive;
return false;
} else {
qLog(Debug) << "Validated drive letter" << drive;
info->mount_point_ = path.ToString();
info->fs_name_ = name.ToString();
info->fs_type_ = type.ToString();
info->fs_serial_ = serial;
return true;
}
}
QStringList WmdmLister::DeviceUniqueIDs() {
QMutexLocker l(&mutex_);
return devices_.keys();
}
QVariantList WmdmLister::DeviceIcons(const QString& id) {
QPixmap pixmap = LockAndGetDeviceInfo(id, &DeviceInfo::icon_);
if (pixmap.isNull())
return QVariantList();
return QVariantList() << pixmap;
}
QString WmdmLister::DeviceManufacturer(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::manufacturer_);
}
QString WmdmLister::DeviceModel(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::name_);
}
quint64 WmdmLister::DeviceCapacity(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::total_bytes_);
}
quint64 WmdmLister::DeviceFreeSpace(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::free_bytes_);
}
QVariantMap WmdmLister::DeviceHardwareInfo(const QString& id) {
QVariantMap ret;
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return ret;
const DeviceInfo& info = devices_[id];
ret[tr("Drive letter")] = QDir::toNativeSeparators(info.mount_point_);
ret[tr("Filesystem type")] = info.fs_type_;
ret[tr("Filesystem name")] = info.fs_name_;
ret[tr("Device name")] = info.device_name_;
ret[tr("Volume name")] = info.volume_name_;
if (info.fs_serial_ != 0)
ret[tr("Filesystem serial number")] = info.fs_serial_;
return ret;
}
QString WmdmLister::MakeFriendlyName(const QString& id) {
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return QString();
const DeviceInfo& info = devices_[id];
if (info.manufacturer_.isEmpty() || info.manufacturer_ == "Unknown")
return info.name_;
return info.manufacturer_ + " " + info.name_;
}
QList<QUrl> WmdmLister::MakeDeviceUrls(const QString& id) {
QList<QUrl> ret;
QString mount_point = LockAndGetDeviceInfo(id, &DeviceInfo::mount_point_);
if (!mount_point.isEmpty()) {
ret << MakeUrlFromLocalPath(mount_point);
}
QUrl wmdm_url;
wmdm_url.setScheme("wmdm");
wmdm_url.setPath(id);
ret << wmdm_url;
return ret;
}
void WmdmLister::UnmountDevice(const QString& id) {
}
void WmdmLister::UpdateDeviceFreeSpace(const QString& id) {
// This needs to be done in the lister's thread where we already have COM
// initialised
metaObject()->invokeMethod(this, "DoUpdateDriveFreeSpace",
Qt::BlockingQueuedConnection, Q_ARG(QString, id));
}
void WmdmLister::DoUpdateDriveFreeSpace(const QString& id) {
{
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return;
UpdateFreeSpace(&devices_[id]);
}
emit DeviceChanged(id);
}
namespace {
qint64 GetSpaceValue(
IWMDMStorageGlobals* globals,
LONG (IWMDMStorageGlobals::*f)(DWORD*,DWORD*)) {
DWORD low, high;
((globals)->*(f))(&low, &high);
return (qint64)high << 32 | (qint64)low;
}
}
void WmdmLister::UpdateFreeSpace(DeviceInfo* info) {
IWMDMStorageGlobals* globals;
info->storage_->GetStorageGlobals(&globals);
DWORD low, high;
globals->GetTotalSize(&low, &high);
info->total_bytes_ = (qint64)high << 32 | (qint64)low;
globals->GetTotalFree(&low, &high);
info->free_bytes_ = (qint64)high << 32 | (qint64)low;
globals->Release();
}
HRESULT WmdmLister::WMDMMessage(DWORD message_type, LPCWSTR name) {
qLog(Debug) << "WMDM message" << message_type << name;
QString canonical_name = QString::fromWCharArray(name).toLower();
switch (message_type) {
case WMDM_MSG_DEVICE_ARRIVAL: WMDMDeviceAdded(canonical_name); break;
case WMDM_MSG_DEVICE_REMOVAL: WMDMDeviceRemoved(canonical_name); break;
}
return S_OK;
}
void WmdmLister::WMDMDeviceAdded(const QString& canonical_name) {
ScopedWCharArray name(canonical_name);
IWMDMDevice* device = NULL;
if (thread_->manager()->GetDeviceFromCanonicalName(name, &device)) {
qLog(Warning) << "Error in GetDeviceFromCanonicalName for" << canonical_name;
return;
}
IWMDMDevice2* device2 = NULL;
if (device->QueryInterface(IID_IWMDMDevice2, (void**) &device2)) {
qLog(Warning) << "Error getting IWMDMDevice2 from device";
device->Release();
return;
}
device->Release();
DeviceInfo info = ReadDeviceInfo(device2);
if (info.is_suitable_) {
QString id = info.unique_id();
{
QMutexLocker l(&mutex_);
devices_[id] = info;
}
emit DeviceAdded(id);
} else {
device2->Release();
}
}
void WmdmLister::WMDMDeviceRemoved(const QString& canonical_name) {
QString id = CanonicalNameToId(canonical_name);
{
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return;
devices_[id].device_->Release();
devices_[id].storage_->Release();
devices_.remove(id);
}
emit DeviceRemoved(id);
}
LONG WmdmLister::QueryInterface(REFIID riid, void** object) {
*object = 0;
if (riid == IID_IUnknown)
*object = (IUnknown*) this;
else if (riid == IID_IWMDMNotification)
*object = (IWMDMNotification*) this;
else
return E_NOINTERFACE;
return S_OK;
}
ULONG WmdmLister::AddRef() {
return 0;
}
ULONG WmdmLister::Release() {
return 0;
}
QString WmdmLister::DeviceCanonicalName(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::canonical_name_);
}

View File

@ -1,141 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WMDMLISTER_H
#define WMDMLISTER_H
#include "devicelister.h"
#include <QMap>
#include <QMutex>
#include <QPixmap>
#include <QUuid>
#include <boost/scoped_ptr.hpp>
#include <mswmdm.h>
#include <sac_shim.h>
#undef LoadIcon
class WmdmThread;
class WmdmLister : public DeviceLister, public IWMDMNotification {
Q_OBJECT
public:
WmdmLister();
~WmdmLister();
// DeviceLister
virtual void Init();
virtual QStringList DeviceUniqueIDs();
virtual QVariantList DeviceIcons(const QString& id);
virtual QString DeviceManufacturer(const QString& id);
virtual QString DeviceModel(const QString& id);
virtual quint64 DeviceCapacity(const QString& id);
virtual quint64 DeviceFreeSpace(const QString& id);
virtual QVariantMap DeviceHardwareInfo(const QString& id);
virtual QString MakeFriendlyName(const QString& id);
virtual QList<QUrl> MakeDeviceUrls(const QString& id);
virtual void UnmountDevice(const QString& id);
// IWMDMNotification
// The __stdcall is *really* important
virtual HRESULT __stdcall WMDMMessage(DWORD message_type, LPCWSTR name);
virtual LONG __stdcall QueryInterface(const IID& riid, void** object);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
// Called by WmdmLister
QString DeviceCanonicalName(const QString& id);
public slots:
virtual void UpdateDeviceFreeSpace(const QString& id);
virtual void ShutDown();
private slots:
virtual void DoUpdateDriveFreeSpace(const QString& id);
virtual void ReallyShutdown();
private:
struct DeviceInfo {
DeviceInfo() : device_(NULL), storage_(NULL), is_suitable_(false),
total_bytes_(0), free_bytes_(0) {}
QString unique_id() const;
IWMDMDevice2* device_;
IWMDMStorage2* storage_;
bool is_suitable_;
QString name_;
QString manufacturer_;
QString canonical_name_;
QPixmap icon_;
quint64 total_bytes_;
quint64 free_bytes_;
// Only valid for filesystem devices
QString mount_point_;
QString fs_name_;
QString fs_type_;
int fs_serial_;
// Information we get by querying win7-style FS devices
QString device_name_;
QString volume_name_;
};
static const QUuid kDeviceProtocolMsc;
DeviceInfo ReadDeviceInfo(IWMDMDevice2* device);
template <typename T>
T LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field);
void UpdateFreeSpace(DeviceInfo* info);
void GuessDriveLetter(DeviceInfo* info);
bool CheckDriveLetter(DeviceInfo* info, const QString& drive);
static QString CanonicalNameToId(const QString& canonical_name);
void WMDMDeviceAdded(const QString& canonical_name);
void WMDMDeviceRemoved(const QString& canonical_name);
private:
boost::scoped_ptr<WmdmThread> thread_;
SacHandle sac_;
DWORD notification_cookie_;
QMutex mutex_;
QMap<QString, DeviceInfo> devices_;
};
template <typename T>
T WmdmLister::LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field) {
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return T();
return devices_[id].*field;
}
#endif // WMDMLISTER_H

View File

@ -1,133 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "devicelister.h"
#include "wmdmdevice.h"
#include "wmdmlister.h"
#include "wmdmloader.h"
#include "wmdmthread.h"
#include "core/taskmanager.h"
#include "library/librarybackend.h"
#include <boost/scoped_array.hpp>
#include <cwchar>
#include <mswmdm.h>
#include <QUuid>
WmdmLoader::WmdmLoader(TaskManager* task_manager, LibraryBackend* backend,
boost::shared_ptr<ConnectedDevice> device)
: QObject(NULL),
device_(device),
task_manager_(task_manager),
backend_(backend)
{
original_thread_ = thread();
}
WmdmLoader::~WmdmLoader() {
}
void WmdmLoader::LoadDatabase() {
int task_id = task_manager_->StartTask(tr("Loading Windows Media device"));
emit TaskStarted(task_id);
boost::scoped_ptr<WmdmThread> thread(new WmdmThread);
// Get the device's canonical name
boost::shared_ptr<WmdmDevice> connected_device =
boost::static_pointer_cast<WmdmDevice>(device_);
WmdmLister* lister = static_cast<WmdmLister*>(connected_device->lister());
QString canonical_name = lister->DeviceCanonicalName(connected_device->unique_id());
IWMDMStorage* storage = thread->GetRootStorage(canonical_name);
QStringList path_components;
RecursiveExploreStorage(storage, &path_components);
storage->Release();
thread.reset();
// Need to remove all the existing songs in the database first
backend_->DeleteSongs(backend_->FindSongsInDirectory(1));
// Add the songs we've just loaded
backend_->AddOrUpdateSongs(songs_);
task_manager_->SetTaskFinished(task_id);
emit LoadFinished();
}
void WmdmLoader::RecursiveExploreStorage(IWMDMStorage* parent, QStringList* path_components) {
IWMDMEnumStorage* child_it = NULL;
parent->EnumStorage(&child_it);
IWMDMStorage* child = NULL;
ULONG num_retreived = 0;
while (child_it->Next(1, &child, &num_retreived) == S_OK && num_retreived == 1) {
const int kMaxLen = 255;
wchar_t name[kMaxLen];
child->GetName(name, kMaxLen);
DWORD attributes = 0;
_WAVEFORMATEX audio_format;
child->GetAttributes(&attributes, &audio_format);
path_components->append(QString::fromWCharArray(name));
if (attributes & WMDM_FILE_ATTR_FILE) {
LoadFile(child, path_components);
} else if (attributes & WMDM_FILE_ATTR_FOLDER) {
RecursiveExploreStorage(child, path_components);
}
path_components->removeLast();
child->Release();
}
child_it->Release();
}
void WmdmLoader::LoadFile(IWMDMStorage* file, const QStringList* path_components) {
// Convert to a IWMDMStorage3 so we can get metadata
IWMDMStorage3* storage3 = NULL;
if (file->QueryInterface(IID_IWMDMStorage3, (void**) &storage3))
return;
// Get the metadata interface
IWMDMMetaData* metadata = NULL;
if (storage3->GetMetadata(&metadata)) {
storage3->Release();
return;
}
storage3->Release();
QUrl url;
url.setScheme("wmdm");
url.setPath(path_components->join("/"));
// Store the metadata in here
Song song;
song.InitFromWmdm(metadata);
song.set_directory_id(1);
song.set_url(url);
metadata->Release();
if (song.is_valid())
songs_ << song;
}

View File

@ -1,63 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WMDMLOADER_H
#define WMDMLOADER_H
#include <QObject>
#include <boost/shared_ptr.hpp>
#include "core/song.h"
class ConnectedDevice;
class LibraryBackend;
class TaskManager;
struct IWMDMStorage;
class WmdmLoader : public QObject {
Q_OBJECT
public:
WmdmLoader(TaskManager* task_manager, LibraryBackend* backend,
boost::shared_ptr<ConnectedDevice> device);
~WmdmLoader();
public slots:
void LoadDatabase();
signals:
void Error(const QString& message);
void TaskStarted(int task_id);
void LoadFinished();
private:
void RecursiveExploreStorage(IWMDMStorage* parent, QStringList* path_components);
void LoadFile(IWMDMStorage* file, const QStringList* path_components);
private:
boost::shared_ptr<ConnectedDevice> device_;
QThread* original_thread_;
TaskManager* task_manager_;
LibraryBackend* backend_;
SongList songs_;
};
#endif // WMDMLOADER_H

View File

@ -1,82 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wmdmprogress.h"
#include <QtDebug>
WmdmProgress::WmdmProgress(const MusicStorage::ProgressFunction& f)
: f_(f),
estimated_(0)
{
}
LONG WmdmProgress::QueryInterface(const IID& riid, void** object) {
*object = 0;
if (riid == IID_IUnknown)
*object = (IUnknown*) this;
else if (riid == IID_IWMDMProgress)
*object = (IWMDMProgress*) this;
else if (riid == IID_IWMDMProgress2)
*object = (IWMDMProgress2*) this;
else if (riid == IID_IWMDMProgress3)
*object = (IWMDMProgress3*) this;
else
return E_NOINTERFACE;
return S_OK;
}
ULONG WmdmProgress::AddRef() {
return 0;
}
ULONG WmdmProgress::Release() {
return 0;
}
HRESULT WmdmProgress::Begin(DWORD estimated_ticks) {
return Begin3(EVENT_WMDM_CONTENT_TRANSFER, estimated_ticks, NULL);
}
HRESULT WmdmProgress::End() {
return End3(EVENT_WMDM_CONTENT_TRANSFER, S_OK, NULL);
}
HRESULT WmdmProgress::Progress(DWORD transpired_ticks) {
return Progress3(EVENT_WMDM_CONTENT_TRANSFER, transpired_ticks, NULL);
}
HRESULT WmdmProgress::End2(HRESULT completion_code) {
return End3(EVENT_WMDM_CONTENT_TRANSFER, completion_code, NULL);
}
HRESULT WmdmProgress::Begin3(GUID, DWORD estimated_ticks, OPAQUECOMMAND*) {
estimated_ = estimated_ticks;
return S_OK;
}
HRESULT WmdmProgress::End3(GUID, HRESULT, OPAQUECOMMAND*) {
return S_OK;
}
HRESULT WmdmProgress::Progress3(GUID, DWORD transpired_ticks, OPAQUECOMMAND*) {
if (estimated_ != 0)
f_(float(transpired_ticks) / estimated_);
return S_OK;
}

View File

@ -1,58 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WMDMPROGRESS_H
#define WMDMPROGRESS_H
#include <mswmdm.h>
#include "core/musicstorage.h"
class WmdmProgress : public IWMDMProgress3 {
public:
WmdmProgress(const MusicStorage::ProgressFunction& f =
MusicStorage::ProgressFunction());
// IUnknown
// The __stdcall is *really* important
virtual LONG __stdcall QueryInterface(const IID& riid, void** object);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
// IWMDMProgress
virtual HRESULT __stdcall Begin(DWORD estimated_ticks);
virtual HRESULT __stdcall End();
virtual HRESULT __stdcall Progress(DWORD transpired_ticks);
// IWMDMProgress2
virtual HRESULT __stdcall End2(HRESULT completion_code);
// IWMDMProgress3
virtual HRESULT __stdcall Begin3(GUID event_id, DWORD estimated_ticks,
OPAQUECOMMAND* context);
virtual HRESULT __stdcall End3(GUID event_id, HRESULT completion_code,
OPAQUECOMMAND* context);
virtual HRESULT __stdcall Progress3(GUID event_id, DWORD transpired_ticks,
OPAQUECOMMAND* context);
private:
MusicStorage::ProgressFunction f_;
DWORD estimated_;
};
#endif // WMDMPROGRESS_H

View File

@ -1,157 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wmdmthread.h"
#include "core/logging.h"
#include "core/utilities.h"
#include <mswmdm.h>
#include <boost/scoped_array.hpp>
#include <QCoreApplication>
#include <QLibrary>
#include <QMutexLocker>
#include <QtDebug>
BYTE abPVK[] = {0x00};
BYTE abCert[] = {0x00};
bool WmdmThread::sIsLoaded = false;
decltype(&CSecureChannelClient_New) WmdmThread::_CSecureChannelClient_New;
decltype(&CSecureChannelClient_Free) WmdmThread::_CSecureChannelClient_Free;
decltype(&CSecureChannelClient_SetCertificate) WmdmThread::_CSecureChannelClient_SetCertificate;
decltype(&CSecureChannelClient_SetInterface) WmdmThread::_CSecureChannelClient_SetInterface;
decltype(&CSecureChannelClient_Authenticate) WmdmThread::_CSecureChannelClient_Authenticate;
WmdmThread::WmdmThread()
: device_manager_(NULL),
sac_(NULL)
{
if (!sIsLoaded) {
return;
}
// Initialise COM
CoInitialize(0);
// Authenticate with WMDM
IComponentAuthenticate* auth;
if (CoCreateInstance(CLSID_MediaDevMgr, NULL, CLSCTX_ALL,
IID_IComponentAuthenticate, (void**) &auth)) {
qLog(Warning) << "Error creating the IComponentAuthenticate interface";
return;
}
sac_ = _CSecureChannelClient_New();
if (_CSecureChannelClient_SetCertificate(
sac_, SAC_CERT_V1, abCert, sizeof(abCert), abPVK, sizeof(abPVK))) {
qLog(Warning) << "Error setting SAC certificate";
return;
}
_CSecureChannelClient_SetInterface(sac_, auth);
if (_CSecureChannelClient_Authenticate(sac_, SAC_PROTOCOL_V1)) {
qLog(Warning) << "Error authenticating with SAC";
return;
}
// Create the device manager
if (auth->QueryInterface(IID_IWMDeviceManager2, (void**)&device_manager_)) {
qLog(Warning) << "Error creating WMDM device manager";
return;
}
}
WmdmThread::~WmdmThread() {
if (device_manager_) {
// Release the device manager
device_manager_->Release();
}
if (sac_) {
// SAC
_CSecureChannelClient_Free(sac_);
}
// Uninitialise COM
CoUninitialize();
}
namespace {
template <typename T>
T Resolve(QLibrary* library, const char* name) {
return reinterpret_cast<T>(library->resolve(name));
}
} // namespace
bool WmdmThread::StaticInit() {
if (!sIsLoaded) {
QLibrary library(QCoreApplication::applicationDirPath() + "/sac_shim.dll");
if (!library.load()) {
return false;
}
_CSecureChannelClient_New = Resolve<decltype(_CSecureChannelClient_New)>(
&library, "CSecureChannelClient_New");
_CSecureChannelClient_Free = Resolve<decltype(_CSecureChannelClient_Free)>(
&library, "CSecureChannelClient_Free");
_CSecureChannelClient_SetCertificate = Resolve<decltype(_CSecureChannelClient_SetCertificate)>(
&library, "CSecureChannelClient_SetCertificate");
_CSecureChannelClient_SetInterface = Resolve<decltype(_CSecureChannelClient_SetInterface)>(
&library, "CSecureChannelClient_SetInterface");
if (_CSecureChannelClient_New &&
_CSecureChannelClient_Free &&
_CSecureChannelClient_SetCertificate &&
_CSecureChannelClient_SetInterface) {
sIsLoaded = true;
return true;
}
}
return false;
}
IWMDMDevice* WmdmThread::GetDeviceByCanonicalName(const QString& device_name) {
ScopedWCharArray name(device_name);
IWMDMDevice* device = NULL;
if (device_manager_->GetDeviceFromCanonicalName(name, &device)) {
qLog(Warning) << "Error in GetDeviceFromCanonicalName for" << device_name;
return NULL;
}
return device;
}
IWMDMStorage* WmdmThread::GetRootStorage(const QString& device_name) {
IWMDMDevice* device = GetDeviceByCanonicalName(device_name);
IWMDMEnumStorage* storage_it = NULL;
device->EnumStorage(&storage_it);
ULONG storage_fetched = 0;
IWMDMStorage* storage = NULL;
storage_it->Next(1, &storage, &storage_fetched);
storage_it->Release();
device->Release();
return storage;
}

View File

@ -1,57 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WMDMTHREAD_H
#define WMDMTHREAD_H
#include <QMutex>
#include <QtGlobal>
#include <sac_shim.h>
struct IWMDeviceManager2;
struct IWMDMDevice;
struct IWMDMStorage;
class WmdmThread {
public:
WmdmThread();
~WmdmThread();
IWMDeviceManager2* manager() const { return device_manager_; }
IWMDMDevice* GetDeviceByCanonicalName(const QString& device_name);
IWMDMStorage* GetRootStorage(const QString& device_name);
static bool StaticInit();
private:
Q_DISABLE_COPY(WmdmThread);
IWMDeviceManager2* device_manager_;
SacHandle sac_;
static decltype(&CSecureChannelClient_New) _CSecureChannelClient_New;
static decltype(&CSecureChannelClient_Free) _CSecureChannelClient_Free;
static decltype(&CSecureChannelClient_SetCertificate) _CSecureChannelClient_SetCertificate;
static decltype(&CSecureChannelClient_SetInterface) _CSecureChannelClient_SetInterface;
static decltype(&CSecureChannelClient_Authenticate) _CSecureChannelClient_Authenticate;
static bool sIsLoaded;
};
#endif // WMDMTHREAD_H

View File

@ -101,7 +101,6 @@ using boost::scoped_ptr;
#ifdef Q_OS_WIN32
# include <qtsparkle/Updater>
# include "devices/wmdmthread.h"
#endif
// Load sqlite plugin on windows and mac.
@ -447,12 +446,6 @@ int main(int argc, char *argv[]) {
mpris::Mpris mpris(&app);
#endif
#ifdef Q_OS_WIN32
if (!WmdmThread::StaticInit()) {
qLog(Warning) << "Failed to initialise SAC shim";
}
#endif
// Window
MainWindow w(&app, tray_icon.get(), &osd);
#ifdef Q_OS_DARWIN