Remove iDevice support as it doesn't work well for modern devices and libgpod shows no signs of updating to support them.

Fixes issue #3683
This commit is contained in:
John Maguire 2013-05-17 15:30:56 +02:00
parent cea41801db
commit 3d434f1c88
18 changed files with 5 additions and 1703 deletions

View File

@ -71,18 +71,15 @@ pkg_check_modules(GSTREAMER_APP gstreamer-app-0.10)
pkg_check_modules(GSTREAMER_BASE gstreamer-base-0.10)
pkg_check_modules(GSTREAMER_CDDA gstreamer-cdda-0.10)
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-0.10)
pkg_check_modules(IMOBILEDEVICE libimobiledevice-1.0)
pkg_check_modules(INDICATEQT indicate-qt)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.7)
pkg_check_modules(LIBXML libxml-2.0)
pkg_check_modules(PLIST libplist)
pkg_check_modules(QCA qca2)
pkg_check_modules(QJSON REQUIRED QJson)
pkg_check_modules(SPOTIFY libspotify>=12.1.45)
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
pkg_check_modules(USBMUXD libusbmuxd)
if (WIN32)
find_package(ZLIB REQUIRED)
@ -233,13 +230,6 @@ optional_component(GIO ON "Devices: GIO device backend"
DEPENDS "Linux or Windows" "NOT APPLE"
)
optional_component(IMOBILEDEVICE ON "Devices: iPod Touch, iPhone, iPad support"
DEPENDS "libimobiledevice" IMOBILEDEVICE_FOUND
DEPENDS "libplist" PLIST_FOUND
DEPENDS "libusbmuxd" USBMUXD_FOUND
DEPENDS "iPod classic support" HAVE_LIBGPOD
)
optional_component(LIBMTP ON "Devices: MTP support"
DEPENDS "libmtp" LIBMTP_FOUND
)
@ -450,10 +440,6 @@ if(WITH_DEBIAN)
add_subdirectory(debian)
endif(WITH_DEBIAN)
if(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
add_subdirectory(gst/afcsrc)
endif(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
if(HAVE_BREAKPAD)
add_subdirectory(3rdparty/google-breakpad)
endif(HAVE_BREAKPAD)

View File

@ -110,6 +110,11 @@ Section "Delete old files" oldfiles
Delete "$INSTDIR\avutil-50.dll"
Delete "$INSTDIR\libcdio-12.dll"
Delete "$INSTDIR\libgcc_s_dw2-1.dll"
; libimobiledevice removal
Delete "$INSTDIR\libimobiledevice-1.dll"
Delete "$INSTDIR\libplist.dll"
Delete "$INSTDIR\libusbmuxd.dll"
SectionEnd
Section "Clementine" Clementine
@ -159,7 +164,6 @@ Section "Clementine" Clementine
File "libgsttag-0.10-0.dll"
File "libgthread-2.0-0.dll"
File "libid3tag.dll"
File "libimobiledevice-1.dll"
File "liblastfm.dll"
File "libmad.dll"
File "libmms-0.dll"
@ -178,7 +182,6 @@ Section "Clementine" Clementine
File "libstdc++-6.dll"
File "libtag.dll"
File "libtasn1-3.dll"
File "libusbmuxd.dll"
File "libvorbis-0.dll"
File "libvorbisenc-2.dll"
File "libxml2-2.dll"

View File

@ -1,27 +0,0 @@
cmake_minimum_required(VERSION 2.6)
set(CMAKE_C_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "-Woverloaded-virtual -Wall")
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${GOBJECT_INCLUDE_DIRS})
include_directories(${GSTREAMER_INCLUDE_DIRS})
include_directories(${IMOBILEDEVICE_INCLUDE_DIRS})
set(SOURCES
gstafcsrc.c
)
add_library(gstafcsrc STATIC
${SOURCES}
)
target_link_libraries(gstafcsrc
${GOBJECT_LIBRARIES}
${GLIB_LIBRARIES}
${GSTREAMER_LIBRARIES}
${GSTREAMER_BASE_LIBRARIES}
${IMOBILEDEVICE_LIBRARIES}
)

View File

@ -1,368 +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 "gstafcsrc.h"
#include <stdlib.h>
#include <string.h>
#include <gst/gst.h>
// Signals
enum {
LAST_SIGNAL
};
// Properties
enum {
PROP_0,
PROP_LOCATION,
};
GST_DEBUG_CATEGORY_STATIC(gst_afc_src_debug);
#define GST_CAT_DEFAULT gst_afc_src_debug
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("ANY")
);
static void gst_afc_src_interface_init(GType type);
static void gst_afc_src_uri_handler_init(gpointer iface, gpointer data);
static void gst_afc_src_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_afc_src_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_afc_src_finalize(GObject* object);
static gboolean gst_afc_src_start(GstBaseSrc* src);
static gboolean gst_afc_src_stop(GstBaseSrc* src);
static GstFlowReturn gst_afc_src_create(GstBaseSrc* src, guint64 offset, guint length, GstBuffer** buffer);
static gboolean gst_afc_src_is_seekable(GstBaseSrc* src);
static gboolean gst_afc_src_get_size(GstBaseSrc* src, guint64* size);
GST_BOILERPLATE_FULL(GstAfcSrc, gst_afc_src, GstBaseSrc, GST_TYPE_BASE_SRC, gst_afc_src_interface_init);
static void gst_afc_src_interface_init(GType type) {
static const GInterfaceInfo urihandler_info = {
gst_afc_src_uri_handler_init,
NULL,
NULL
};
GST_DEBUG_CATEGORY_INIT (gst_afc_src_debug, "afcsrc", 0, "iPod/iPhone Source");
g_type_add_interface_static(type, GST_TYPE_URI_HANDLER, &urihandler_info);
}
static void gst_afc_src_base_init(gpointer gclass) {
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
gst_element_class_set_details_simple(element_class,
"iPod/iPhone Source",
"Source/Afc",
"Read using libimobiledevice",
"David Sansome <me@davidsansome.com>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
}
static void gst_afc_src_class_init (GstAfcSrcClass* klass) {
GObjectClass* gobject_class = (GObjectClass*) klass;
GstBaseSrcClass* gstbasesrc_class = (GstBaseSrcClass*) klass;
gobject_class->set_property = gst_afc_src_set_property;
gobject_class->get_property = gst_afc_src_get_property;
gobject_class->finalize = gst_afc_src_finalize;
g_object_class_install_property(gobject_class, PROP_LOCATION,
g_param_spec_string(
"location", "URI",
"The URI of the file to read, must be of the form afc://uuid/filename", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY));
gstbasesrc_class->start = gst_afc_src_start;
gstbasesrc_class->stop = gst_afc_src_stop;
gstbasesrc_class->create = gst_afc_src_create;
gstbasesrc_class->is_seekable = gst_afc_src_is_seekable;
gstbasesrc_class->get_size = gst_afc_src_get_size;
}
static GstURIType gst_afc_src_uri_get_type() {
return GST_URI_SRC;
}
static gchar** gst_afc_src_uri_get_protocols() {
static const gchar* protocols[] = { "afc", NULL };
return (char**) protocols;
}
static const gchar* gst_afc_src_uri_get_uri(GstURIHandler* handler) {
GstAfcSrc* self = GST_AFCSRC(handler);
return self->location_;
}
static gboolean gst_afc_src_uri_set_uri(GstURIHandler* handler, const gchar* uri) {
GstAfcSrc* self = GST_AFCSRC(handler);
self->location_ = g_strdup(uri);
return TRUE;
}
static void gst_afc_src_uri_handler_init(gpointer g_iface, gpointer data) {
GstURIHandlerInterface* iface = (GstURIHandlerInterface*) g_iface;
iface->get_type = gst_afc_src_uri_get_type;
iface->get_protocols = gst_afc_src_uri_get_protocols;
iface->set_uri = gst_afc_src_uri_set_uri;
iface->get_uri = gst_afc_src_uri_get_uri;
}
static void gst_afc_src_init(GstAfcSrc* element, GstAfcSrcClass* gclass) {
element->location_ = NULL;
element->uuid_ = NULL;
element->path_ = NULL;
element->connected_ = FALSE;
element->afc_ = NULL;
element->afc_port_ = 0;
element->device_ = NULL;
element->file_handle_ = 0;
element->buffer_ = NULL;
element->buffer_is_valid_ = FALSE;
element->buffer_length_ = 0;
element->buffer_offset_ = 0;
}
static void gst_afc_src_finalize(GObject* object) {
GstAfcSrc* self = GST_AFCSRC(object);
free(self->location_);
free(self->uuid_);
free(self->path_);
free(self->buffer_);
if (self->file_handle_)
afc_file_close(self->afc_, self->file_handle_);
if (self->afc_)
afc_client_free(self->afc_);
if (self->device_)
idevice_free(self->device_);
G_OBJECT_CLASS(parent_class)->finalize(object);
}
static void gst_afc_src_set_property(
GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) {
GstAfcSrc* self = GST_AFCSRC(object);
switch (prop_id) {
case PROP_LOCATION:
self->location_ = g_strdup(g_value_get_string(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void gst_afc_src_get_property(
GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) {
GstAfcSrc* self = GST_AFCSRC(object);
switch (prop_id) {
case PROP_LOCATION:
g_value_set_string(value, self->location_);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean gst_afc_src_start(GstBaseSrc* src) {
GstAfcSrc* self = GST_AFCSRC(src);
// Don't connect again
if (self->connected_)
return TRUE;
// Check that a URI has been passed
if (!self->location_ || self->location_[0] == '\0') {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("No URI specified"), (NULL));
return FALSE;
}
// Parse the URI
// HERE BE DRAGONS
gchar* location = gst_uri_get_location(self->location_);
char* path_pos = strstr(location, "/");
self->uuid_ = (char*) malloc(path_pos - location + 1);
memcpy(self->uuid_, location, path_pos - location);
self->uuid_[path_pos - location] = '\0';
self->path_ = g_strdup(path_pos);
g_free(location);
// Open the device
idevice_error_t err = idevice_new(&self->device_, self->uuid_);
if (err != IDEVICE_E_SUCCESS) {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("idevice error: %d", err), (NULL));
return FALSE;
}
lockdownd_client_t lockdown;
lockdownd_error_t lockdown_err =
lockdownd_client_new_with_handshake(self->device_, &lockdown, "GstAfcSrc");
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("lockdown error: %d", lockdown_err), (NULL));
return FALSE;
}
lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &self->afc_port_);
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("lockdown error: %d", lockdown_err), (NULL));
lockdownd_client_free(lockdown);
return FALSE;
}
afc_error_t afc_err = afc_client_new(self->device_, self->afc_port_, &self->afc_);
if (afc_err != 0) {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", afc_err), (NULL));
lockdownd_client_free(lockdown);
return FALSE;
}
lockdownd_client_free(lockdown);
// Try opening the file
afc_err = afc_file_open(self->afc_, self->path_, AFC_FOPEN_RDONLY, &self->file_handle_);
if (afc_err != 0) {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", afc_err), (NULL));
return FALSE;
}
self->connected_ = TRUE;
return TRUE;
}
static gboolean gst_afc_src_stop(GstBaseSrc* src) {
return TRUE;
}
static GstFlowReturn gst_afc_src_create(GstBaseSrc* src, guint64 offset, guint length, GstBuffer** buffer) {
GstAfcSrc* self = GST_AFCSRC(src);
GstBuffer* buf = gst_buffer_try_new_and_alloc(length);
if (buf == NULL && length > 0) {
GST_ERROR_OBJECT(src, "Failed to allocate %u bytes", length);
return GST_FLOW_ERROR;
}
// Is this section within our cache?
if (!self->buffer_is_valid_ || offset < self->buffer_offset_ ||
offset + length > self->buffer_offset_ + self->buffer_length_) {
// No - read from the device to fill our internal cache.
// Always read twice the requested size so the next read(s) hit the cache too.
if (self->buffer_length_ != length * 2) {
self->buffer_ = (char*)realloc(self->buffer_, length * 2);
self->buffer_is_valid_ = TRUE;
self->buffer_length_ = length * 2;
}
self->buffer_offset_ = offset;
uint32_t bytes_read = 0;
afc_error_t err;
err = afc_file_seek(self->afc_, self->file_handle_, offset, SEEK_SET);
if (err == AFC_E_SUCCESS) {
err = afc_file_read(self->afc_, self->file_handle_, self->buffer_, length * 2, &bytes_read);
}
if (err != AFC_E_SUCCESS) {
gst_buffer_unref(buf);
return GST_FLOW_ERROR;
}
}
// Copy from our internal buffer to the output buffer
memcpy(GST_BUFFER_DATA(buf), self->buffer_ + offset - self->buffer_offset_, length);
*buffer = buf;
return GST_FLOW_OK;
}
static gboolean gst_afc_src_is_seekable(GstBaseSrc* src) {
return TRUE;
}
static gboolean gst_afc_src_get_size(GstBaseSrc* src, guint64* size) {
GstAfcSrc* self = GST_AFCSRC(src);
char** infolist = NULL;
afc_error_t err = afc_get_file_info(self->afc_, self->path_, &infolist);
if (err != AFC_E_SUCCESS || !infolist) {
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", err), (NULL));
return FALSE;
}
gboolean found_size = FALSE;
char** p = infolist;
while (*p != NULL) {
if (g_strcmp0(*p, "st_size") == 0) {
*size = strtoll(*(p+1), NULL, 0);
found_size = TRUE;
}
free(*p); ++p;
free(*p); ++p;
}
free(infolist);
if (!found_size) {
*size = 0;
return FALSE;
}
return TRUE;
}
#define PACKAGE "Clementine"
static gboolean afcsrc_init(GstPlugin* afcsrc) {
return gst_element_register(afcsrc, "afcsrc", GST_RANK_PRIMARY, GST_TYPE_AFCSRC);
}
void afcsrc_register_static() {
gst_plugin_register_static(
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"afcsrc",
"iPod/iPhone Source",
afcsrc_init,
"0.1",
"GPL",
"Clementine",
"Clementine",
"http://www.clementine-player.org/");
}

View File

@ -1,81 +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 __GST_AFCSRC_H__
#define __GST_AFCSRC_H__
#include <gst/gst.h>
#include <gst/base/gstbasesrc.h>
#include <libimobiledevice/afc.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#ifdef __cplusplus
extern "C" {
#endif
void afcsrc_register_static();
#ifdef __cplusplus
}
#endif
G_BEGIN_DECLS
#define GST_TYPE_AFCSRC \
(gst_afc_src_get_type())
#define GST_AFCSRC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AFCSRC,GstAfcSrc))
#define GST_AFCSRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AFCSRC,GstAfcSrcClass))
#define GST_IS_AFCSRC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AFCSRC))
#define GST_IS_AFCSRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AFCSRC))
typedef struct _GstAfcSrc GstAfcSrc;
typedef struct _GstAfcSrcClass GstAfcSrcClass;
struct _GstAfcSrc {
GstBaseSrc element;
char* location_;
char* uuid_;
char* path_;
gboolean connected_;
idevice_t device_;
afc_client_t afc_;
uint16_t afc_port_;
uint64_t file_handle_;
gboolean buffer_is_valid_;
guint64 buffer_offset_;
guint buffer_length_;
char* buffer_;
};
struct _GstAfcSrcClass {
GstBaseSrcClass parent_class;
};
GType gst_afc_src_get_type (void);
G_END_DECLS
#endif

View File

@ -26,10 +26,6 @@ if (QT_VERSION_MINOR GREATER 5)
endif(QT_VERSION_MINOR GREATER 5)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
if(ENABLE_IMOBILEDEVICE AND IMOBILEDEVICE_VERSION VERSION_GREATER 1.1.1)
set(IMOBILEDEVICE_USES_UDIDS ON)
endif()
include_directories(${CMAKE_BINARY_DIR})
include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${LIBXML_INCLUDE_DIRS})
@ -1035,25 +1031,6 @@ optional_source(HAVE_AUDIOCD
devices/cddalister.h
)
# libimobiledevice backend and device
optional_source(HAVE_IMOBILEDEVICE
INCLUDE_DIRECTORIES
${IMOBILEDEVICE_INCLUDE_DIRS}
${PLIST_INCLUDE_DIRS}
${PLISTPP_INCLUDE_DIRS}
SOURCES
devices/afcdevice.cpp
devices/afcfile.cpp
devices/afctransfer.cpp
devices/ilister.cpp
devices/imobiledeviceconnection.cpp
HEADERS
devices/afcdevice.h
devices/afcfile.h
devices/afctransfer.h
devices/ilister.h
)
# mtp device
optional_source(HAVE_LIBMTP
INCLUDE_DIRECTORIES ${LIBMTP_INCLUDE_DIRS}
@ -1255,17 +1232,6 @@ if(HAVE_AUDIOCD)
target_link_libraries(clementine_lib ${GSTREAMER_CDDA_LIBRARIES})
endif(HAVE_AUDIOCD)
if(HAVE_IMOBILEDEVICE)
target_link_libraries(clementine_lib
${IMOBILEDEVICE_LIBRARIES}
${PLIST_LIBRARIES}
${USBMUXD_LIBRARIES}
gstafcsrc
)
link_directories(${IMOBILEDEVICE_LIBRARY_DIRS})
link_directories(${USBMUXD_LIBRARY_DIRS})
endif(HAVE_IMOBILEDEVICE)
if(HAVE_MOODBAR)
target_link_libraries(clementine_lib gstmoodbar)
endif()

View File

@ -1,176 +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 "afcdevice.h"
#include "afcfile.h"
#include "afctransfer.h"
#include "devicemanager.h"
#include "gpodloader.h"
#include "imobiledeviceconnection.h"
#include "core/application.h"
#include "core/utilities.h"
#include <QThread>
AfcDevice::AfcDevice(
const QUrl& url, DeviceLister* lister, const QString& unique_id,
DeviceManager* manager, Application* app, int database_id, bool first_time)
: GPodDevice(url, lister, unique_id, manager, app, database_id, first_time),
transfer_(NULL)
{
}
AfcDevice::~AfcDevice() {
Utilities::RemoveRecursive(local_path_);
}
void AfcDevice::Init() {
// Make a new temporary directory for the iTunesDB. We copy it off the iPod
// so that libgpod can have a local directory to use.
local_path_ = Utilities::MakeTempDir();
InitBackendDirectory(local_path_, first_time_, false);
model_->Init();
transfer_ = new AfcTransfer(url_.host(), local_path_, app_->task_manager(),
shared_from_this());
transfer_->moveToThread(loader_thread_);
connect(transfer_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(transfer_, SIGNAL(CopyFinished(bool)), SLOT(CopyFinished(bool)));
connect(loader_thread_, SIGNAL(started()), transfer_, SLOT(CopyFromDevice()));
loader_thread_->start();
}
void AfcDevice::CopyFinished(bool success) {
transfer_->deleteLater();
transfer_ = NULL;
if (!success) {
app_->AddError(tr("An error occurred copying the iTunes database from the device"));
return;
}
// Now load the songs from the local database
loader_ = new GPodLoader(local_path_, app_->task_manager(), backend_,
shared_from_this());
loader_->set_music_path_prefix("afc://" + url_.host());
loader_->set_song_type(Song::Type_Stream);
loader_->moveToThread(loader_thread_);
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*)), SLOT(LoadFinished(Itdb_iTunesDB*)));
QMetaObject::invokeMethod(loader_, "LoadDatabase");
}
bool AfcDevice::StartCopy(QList<Song::FileType>* supported_types) {
GPodDevice::StartCopy(supported_types);
connection_.reset(new iMobileDeviceConnection(url_.host()));
return true;
}
bool AfcDevice::CopyToStorage(const CopyJob& job) {
Q_ASSERT(db_);
Itdb_Track* track = AddTrackToITunesDb(job.metadata_);
// Get an unused filename on the device
QString dest = connection_->GetUnusedFilename(db_, job.metadata_);
if (dest.isEmpty()) {
itdb_track_remove(track);
return false;
}
// Copy the file
{
QFile source_file(job.source_);
AfcFile dest_file(connection_.get(), dest);
if (!Utilities::Copy(&source_file, &dest_file))
return false;
}
track->transferred = 1;
// Set the filetype_marker
QString suffix = dest.section('.', -1, -1).toUpper();
track->filetype_marker = 0;
for (int i=0 ; i<4 ; ++i) {
track->filetype_marker = track->filetype_marker << 8;
if (i >= suffix.length())
track->filetype_marker |= ' ';
else
track->filetype_marker |= suffix[i].toAscii();
}
// Set the filename
track->ipod_path = strdup(dest.toUtf8().constData());
itdb_filename_fs2ipod(track->ipod_path);
AddTrackToModel(track, "afc://" + url_.host());
// Remove the original if it was requested
if (job.remove_original_) {
QFile::remove(job.source_);
}
return true;
}
void AfcDevice::FinishCopy(bool success) {
// Temporarily unset the GUID so libgpod doesn't lock the device for syncing
itdb_device_set_sysinfo(db_->device, "FirewireGuid", NULL);
GPodDevice::FinishCopy(success);
// Close the connection to the device
connection_.reset();
}
void AfcDevice::FinaliseDatabase() {
// Set the GUID again to lock the device for syncing
itdb_device_set_sysinfo(db_->device, "FirewireGuid", url_.host().toUtf8().constData());
// Copy the files back to the iPod
// No need to start another thread since we're already in the organiser thread
AfcTransfer transfer(url_.host(), local_path_, NULL, shared_from_this());
itdb_start_sync(db_);
bool success = transfer.CopyToDevice(connection_.get());
itdb_stop_sync(db_);
if (!success) {
app_->AddError(tr("An error occurred copying the iTunes database onto the device"));
return;
}
}
bool AfcDevice::DeleteFromStorage(const DeleteJob& job) {
const QString path = job.metadata_.url().toLocalFile();
if (!RemoveTrackFromITunesDb(path))
return false;
// Remove the file
if (afc_remove_path(connection_->afc(), path.toUtf8().constData()) != AFC_E_SUCCESS)
return false;
// Remove it from our library model
songs_to_remove_ << job.metadata_;
return true;
}

View File

@ -1,70 +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 AFCDEVICE_H
#define AFCDEVICE_H
#include "gpoddevice.h"
#include <QMutex>
#include <QWaitCondition>
#include <boost/scoped_ptr.hpp>
#include <gpod/itdb.h>
class AfcTransfer;
class GPodLoader;
class iMobileDeviceConnection;
class AfcDevice : public GPodDevice {
Q_OBJECT
public:
Q_INVOKABLE AfcDevice(const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
Application* app,
int database_id, bool first_time);
~AfcDevice();
void Init();
static QStringList url_schemes() { return QStringList() << "afc"; }
bool StartCopy(QList<Song::FileType>* supported_types);
bool CopyToStorage(const CopyJob& job);
void FinishCopy(bool success);
bool DeleteFromStorage(const DeleteJob& job);
protected:
void FinaliseDatabase();
private slots:
void CopyFinished(bool success);
private:
void RemoveRecursive(const QString& path);
private:
AfcTransfer* transfer_;
boost::scoped_ptr<iMobileDeviceConnection> connection_;
QString local_path_;
};
#endif // AFCDEVICE_H

View File

@ -1,79 +0,0 @@
#include "afcfile.h"
#include "imobiledeviceconnection.h"
#include <libimobiledevice/afc.h>
AfcFile::AfcFile(iMobileDeviceConnection* connection, const QString& path, QObject* parent)
: QIODevice(parent),
connection_(connection),
handle_(0),
path_(path)
{
}
AfcFile::~AfcFile() {
close();
}
bool AfcFile::open(QIODevice::OpenMode mode) {
afc_file_mode_t afc_mode;
switch (mode) {
case ReadOnly:
afc_mode = AFC_FOPEN_RDONLY;
break;
case WriteOnly:
afc_mode = AFC_FOPEN_WRONLY;
break;
case ReadWrite:
afc_mode = AFC_FOPEN_RW;
break;
default:
afc_mode = AFC_FOPEN_RW;
}
afc_error_t err = afc_file_open(
connection_->afc(), path_.toUtf8().constData(), afc_mode, &handle_);
if (err != AFC_E_SUCCESS) {
return false;
}
return QIODevice::open(mode);
}
void AfcFile::close() {
if (handle_) {
afc_file_close(connection_->afc(), handle_);
}
QIODevice::close();
}
bool AfcFile::seek(qint64 pos) {
afc_error_t err = afc_file_seek(connection_->afc(), handle_, pos, SEEK_SET);
if (err != AFC_E_SUCCESS) {
return false;
}
QIODevice::seek(pos);
return true;
}
qint64 AfcFile::readData(char* data, qint64 max_size) {
uint32_t bytes_read = 0;
afc_error_t err = afc_file_read(connection_->afc(), handle_, data, max_size, &bytes_read);
if (err != AFC_E_SUCCESS) {
return -1;
}
return bytes_read;
}
qint64 AfcFile::writeData(const char* data, qint64 max_size) {
uint32_t bytes_written = 0;
afc_error_t err = afc_file_write(connection_->afc(), handle_, data, max_size, &bytes_written);
if (err != AFC_E_SUCCESS) {
return -1;
}
return bytes_written;
}
qint64 AfcFile::size() const {
return connection_->GetFileInfo(path_, "st_size").toLongLong();
}

View File

@ -1,36 +0,0 @@
#ifndef AFCFILE_H
#define AFCFILE_H
#include <stdint.h>
#include <QIODevice>
#include <libimobiledevice/afc.h>
class iMobileDeviceConnection;
class AfcFile : public QIODevice {
Q_OBJECT
public:
AfcFile(iMobileDeviceConnection* connection, const QString& path, QObject* parent = 0);
~AfcFile();
// QIODevice
void close();
bool open(OpenMode mode);
bool seek(qint64 pos);
qint64 size() const;
private:
// QIODevice
qint64 readData(char* data, qint64 max_size);
qint64 writeData(const char* data, qint64 max_size);
iMobileDeviceConnection* connection_;
uint64_t handle_;
QString path_;
};
#endif

View File

@ -1,135 +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 "afcfile.h"
#include "afctransfer.h"
#include "imobiledeviceconnection.h"
#include "core/taskmanager.h"
#include "core/utilities.h"
#include <QDir>
#include <QtDebug>
#include <boost/scoped_ptr.hpp>
AfcTransfer::AfcTransfer(const QString& uuid, const QString& local_destination,
TaskManager* task_manager, boost::shared_ptr<ConnectedDevice> device)
: QObject(NULL),
device_(device),
task_manager_(task_manager),
uuid_(uuid),
local_destination_(local_destination)
{
original_thread_ = thread();
important_directories_ << "/iTunes_Control/Artwork";
important_directories_ << "/iTunes_Control/Device";
important_directories_ << "/iTunes_Control/iTunes";
}
AfcTransfer::~AfcTransfer() {
}
void AfcTransfer::CopyFromDevice() {
int task_id = 0;
if (task_manager_) {
task_id = task_manager_->StartTask(tr("Copying iPod database"));
emit TaskStarted(task_id);
}
// Connect to the device
iMobileDeviceConnection c(uuid_);
// Copy directories. If one fails we stop.
bool success = true;
foreach (const QString& dir, important_directories_) {
if (!CopyDirFromDevice(&c, dir)) {
success = false;
break;
}
}
if (task_manager_) {
moveToThread(original_thread_);
task_manager_->SetTaskFinished(task_id);
emit CopyFinished(success);
}
}
bool AfcTransfer::CopyToDevice(iMobileDeviceConnection* connection) {
// Connect to the device
if (!connection)
connection = new iMobileDeviceConnection(uuid_);
foreach (const QString& dir, important_directories_)
if (!CopyDirToDevice(connection, dir))
return false;
return true;
}
bool AfcTransfer::CopyDirFromDevice(iMobileDeviceConnection* c, const QString& path) {
foreach (const QString& filename, c->ReadDirectory(path, QDir::Files | QDir::NoDotAndDotDot)) {
if (!CopyFileFromDevice(c, path + "/" + filename))
return false;
}
foreach (const QString& dir, c->ReadDirectory(path, QDir::Dirs | QDir::NoDotAndDotDot)) {
if (!CopyDirFromDevice(c, path + "/" + dir))
return false;
}
return true;
}
bool AfcTransfer::CopyDirToDevice(iMobileDeviceConnection* c, const QString& path) {
QDir dir(local_destination_ + path);
if (!c->Exists(path)) {
c->MkDir(path);
}
foreach (const QString& filename, dir.entryList(QDir::Files | QDir::NoDotAndDotDot)) {
if (!CopyFileToDevice(c, path + "/" + filename))
return false;
}
foreach (const QString& dir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
if (!CopyDirToDevice(c, path + "/" + dir))
return false;
}
return true;
}
bool AfcTransfer::CopyFileFromDevice(iMobileDeviceConnection *c, const QString &path) {
QString local_filename = local_destination_ + path;
QString local_dir = local_filename.section('/', 0, -2);
QDir d;
d.mkpath(local_dir);
QFile dest(local_filename);
AfcFile source(c, path);
return Utilities::Copy(&source, &dest);
}
bool AfcTransfer::CopyFileToDevice(iMobileDeviceConnection *c, const QString &path) {
QFile source(local_destination_ + path);
AfcFile dest(c, path);
return Utilities::Copy(&source, &dest);
}

View File

@ -1,66 +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 AFCTRANSFER_H
#define AFCTRANSFER_H
#include <QObject>
#include <QStringList>
#include <boost/shared_ptr.hpp>
class ConnectedDevice;
class iMobileDeviceConnection;
class TaskManager;
class QIODevice;
class AfcTransfer : public QObject {
Q_OBJECT
public:
AfcTransfer(const QString& uuid, const QString& local_destination,
TaskManager* task_manager, boost::shared_ptr<ConnectedDevice> device);
~AfcTransfer();
bool CopyToDevice(iMobileDeviceConnection* connection);
public slots:
void CopyFromDevice();
signals:
void TaskStarted(int task_id);
void CopyFinished(bool success);
private:
bool CopyDirFromDevice(iMobileDeviceConnection* c, const QString& path);
bool CopyDirToDevice(iMobileDeviceConnection* c, const QString& path);
bool CopyFileFromDevice(iMobileDeviceConnection* c, const QString& path);
bool CopyFileToDevice(iMobileDeviceConnection* c, const QString& path);
private:
boost::shared_ptr<ConnectedDevice> device_;
QThread* original_thread_;
TaskManager* task_manager_;
QString uuid_;
QString local_destination_;
QStringList important_directories_;
};
#endif // AFCTRANSFER_H

View File

@ -60,10 +60,6 @@
#ifdef HAVE_GIO
# include "giolister.h"
#endif
#ifdef HAVE_IMOBILEDEVICE
# include "afcdevice.h"
# include "ilister.h"
#endif
#ifdef HAVE_LIBMTP
# include "mtpdevice.h"
#endif
@ -210,10 +206,6 @@ DeviceManager::DeviceManager(Application* app, QObject *parent)
AddLister(new WmdmLister);
AddDeviceClass<WmdmDevice>();
#endif
#ifdef HAVE_IMOBILEDEVICE
AddLister(new iLister);
AddDeviceClass<AfcDevice>();
#endif
AddDeviceClass<FilesystemDevice>();

View File

@ -1,208 +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 "config.h"
#include "ilister.h"
#include "imobiledeviceconnection.h"
#include <QStringList>
#include <QtDebug>
iLister::iLister() {
}
iLister::~iLister() {
}
void iLister::Init() {
idevice_event_subscribe(&EventCallback, reinterpret_cast<void*>(this));
}
void iLister::EventCallback(const idevice_event_t* event, void* context) {
iLister* me = reinterpret_cast<iLister*>(context);
#ifdef IMOBILEDEVICE_USES_UDIDS
const char* uuid = event->udid;
#else
const char* uuid = event->uuid;
#endif
switch (event->event) {
case IDEVICE_DEVICE_ADD:
me->DeviceAddedCallback(uuid);
break;
case IDEVICE_DEVICE_REMOVE:
me->DeviceRemovedCallback(uuid);
break;
}
}
void iLister::DeviceAddedCallback(const char* uuid) {
DeviceInfo info = ReadDeviceInfo(uuid);
QString id = UniqueId(uuid);
QString name = MakeFriendlyName(id);
if (info.product_type == "iPhone 3,1" || info.product_type.startsWith("iPad")) {
// iPhone 4 and iPad unsupported by libgpod as of 0.7.94.
return;
}
{
QMutexLocker l(&mutex_);
devices_[id] = info;
}
emit DeviceAdded(id);
}
void iLister::DeviceRemovedCallback(const char* uuid) {
QString id = UniqueId(uuid);
{
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return;
devices_.remove(id);
}
emit DeviceRemoved(id);
}
QString iLister::UniqueId(const char *uuid) {
return "ithing/" + QString::fromUtf8(uuid);
}
QStringList iLister::DeviceUniqueIDs() {
return devices_.keys();
}
QVariantList iLister::DeviceIcons(const QString& id) {
return QVariantList() << "ipodtouchicon";
}
QString iLister::DeviceManufacturer(const QString& id) {
return "Apple";
}
QString iLister::DeviceModel(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::product_type);
}
quint64 iLister::DeviceCapacity(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::total_bytes);
}
quint64 iLister::DeviceFreeSpace(const QString& id) {
return LockAndGetDeviceInfo(id, &DeviceInfo::free_bytes);
}
QVariantMap iLister::DeviceHardwareInfo(const QString& id) {
QVariantMap ret;
ret[tr("Color")] = LockAndGetDeviceInfo(id, &DeviceInfo::colour);
ret["IMEI"] = LockAndGetDeviceInfo(id, &DeviceInfo::imei);
ret[tr("Password Protected")] = LockAndGetDeviceInfo(id, &DeviceInfo::password_protected);
ret[tr("Timezone")] = LockAndGetDeviceInfo(id, &DeviceInfo::timezone);
ret[tr("WiFi MAC Address")] = LockAndGetDeviceInfo(id, &DeviceInfo::wifi_mac);
ret[tr("Bluetooth MAC Address")] = LockAndGetDeviceInfo(id, &DeviceInfo::bt_mac);
return ret;
}
QString iLister::MakeFriendlyName(const QString& id) {
QString name = LockAndGetDeviceInfo(id, &DeviceInfo::name);
if (!name.isEmpty()) {
return name;
}
QString model_id = LockAndGetDeviceInfo(id, &DeviceInfo::product_type);
if (model_id.startsWith("iPhone")) {
QString version = model_id.right(3);
QChar major = version[0];
QChar minor = version[2];
if (major == '1' && minor == '1') {
return "iPhone";
}
if (major == '1' && minor == '2') {
return "iPhone 3G";
}
if (major == '2' && minor == '1') {
return "iPhone 3GS";
}
if (major == '3' && minor == '1') {
return "iPhone 4";
}
} else if (model_id.startsWith("iPod")) {
return "iPod Touch";
} else if (model_id.startsWith("iPad")) {
return "iPad";
}
return model_id;
}
QList<QUrl> iLister::MakeDeviceUrls(const QString& id) {
QList<QUrl> ret;
QString uuid = LockAndGetDeviceInfo(id, &DeviceInfo::uuid);
if (uuid.isEmpty())
return ret;
ret << QUrl("afc://" + uuid + "/");
return ret;
}
void iLister::UnmountDevice(const QString& id) { }
iLister::DeviceInfo iLister::ReadDeviceInfo(const char* uuid) {
DeviceInfo ret;
iMobileDeviceConnection conn(uuid);
ret.uuid = uuid;
ret.product_type = conn.GetProperty("ProductType").toString();
ret.free_bytes = conn.GetProperty("AmountDataAvailable", "com.apple.disk_usage").toULongLong();
ret.total_bytes = conn.GetProperty("TotalDataCapacity", "com.apple.disk_usage").toULongLong();
ret.name = conn.GetProperty("DeviceName").toString();
ret.colour = conn.GetProperty("DeviceColor").toString();
ret.imei = conn.GetProperty("InternationalMobileEquipmentIdentity").toString();
ret.hardware = conn.GetProperty("HardwareModel").toString();
ret.password_protected = conn.GetProperty("PasswordProtected").toBool();
ret.os_version = conn.GetProperty("ProductVersion").toString();
ret.timezone = conn.GetProperty("TimeZone").toString();
ret.wifi_mac = conn.GetProperty("WiFiAddress").toString();
ret.bt_mac = conn.GetProperty("BluetoothAddress").toString();
return ret;
}
void iLister::UpdateDeviceFreeSpace(const QString& id) {
{
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return;
DeviceInfo& info = devices_[id];
iMobileDeviceConnection conn(info.uuid);
info.free_bytes = conn.GetProperty("AmountDataAvailable", "com.apple.disk_usage").toULongLong();
}
emit DeviceChanged(id);
}

View File

@ -1,97 +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 ILISTER_H
#define ILISTER_H
#include "devicelister.h"
#include <libimobiledevice/libimobiledevice.h>
#include <QMutex>
class iLister : public DeviceLister {
Q_OBJECT
public:
iLister();
~iLister();
int priority() const { return 120; }
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);
public slots:
virtual void UpdateDeviceFreeSpace(const QString& id);
private:
struct DeviceInfo {
DeviceInfo() : free_bytes(0), total_bytes(0) {}
QString uuid;
QString product_type;
quint64 free_bytes;
quint64 total_bytes;
QString name; // Name given to the iDevice by the user.
// Extra information.
QString colour;
QString imei;
QString hardware;
bool password_protected;
QString os_version;
QString timezone;
QString wifi_mac;
QString bt_mac;
};
virtual void Init();
static void EventCallback(const idevice_event_t* event, void* context);
void DeviceAddedCallback(const char* uuid);
void DeviceRemovedCallback(const char* uuid);
DeviceInfo ReadDeviceInfo(const char* uuid);
static QString UniqueId(const char* uuid);
template <typename T>
T LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field);
private:
QMutex mutex_;
QMap<QString, DeviceInfo> devices_;
};
template <typename T>
T iLister::LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field) {
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
return T();
return devices_[id].*field;
}
#endif

View File

@ -1,237 +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 "imobiledeviceconnection.h"
#include "core/logging.h"
#include <plist/plist.h>
#include <QCoreApplication>
#include <QUrl>
#include <QtDebug>
iMobileDeviceConnection::iMobileDeviceConnection(const QString& uuid)
: device_(NULL), afc_(NULL), afc_port_(0) {
idevice_error_t err = idevice_new(&device_, uuid.toUtf8().constData());
if (err != IDEVICE_E_SUCCESS) {
qLog(Warning) << "idevice error:" << err;
return;
}
lockdownd_client_t lockdown;
QByteArray label_ascii = QCoreApplication::applicationName().toAscii();
const char* label = label_ascii.constData();
lockdownd_error_t lockdown_err =
lockdownd_client_new_with_handshake(device_, &lockdown, label);
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
qLog(Warning) << "lockdown error:" << lockdown_err;
return;
}
lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &afc_port_);
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
qLog(Warning) << "lockdown error:" << lockdown_err;
lockdownd_client_free(lockdown);
return;
}
afc_error_t afc_err = afc_client_new(device_, afc_port_, &afc_);
if (afc_err != 0) {
qLog(Warning) << "afc error:" << afc_err;
lockdownd_client_free(lockdown);
return;
}
lockdownd_client_free(lockdown);
}
iMobileDeviceConnection::~iMobileDeviceConnection() {
if (afc_) {
afc_client_free(afc_);
}
if (device_) {
idevice_free(device_);
}
}
template <typename T, typename F>
T GetPListValue(plist_t node, F f) {
T ret;
f(node, &ret);
return ret;
}
QVariant iMobileDeviceConnection::GetProperty(const QString& property, const QString& domain) {
lockdownd_client_t lockdown;
QByteArray label_ascii = QCoreApplication::applicationName().toAscii();
const char* label = label_ascii.constData();
lockdownd_error_t lockdown_err =
lockdownd_client_new_with_handshake(device_, &lockdown, label);
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
qLog(Warning) << "lockdown error:" << lockdown_err;
return QVariant();
}
plist_t node = NULL;
QByteArray domain_ascii = domain.toAscii();
const char* d = domain_ascii.isEmpty() ? NULL : domain_ascii.constData();
//const char* d = domain.isEmpty() ? NULL : "com.apple.disk_usage";
lockdownd_get_value(lockdown, d, property.toAscii().constData(), &node);
lockdownd_client_free(lockdown);
if (!node) {
qLog(Warning) << "get_value failed" << property << domain;
return QVariant();
}
switch (plist_get_node_type(node)) {
case PLIST_BOOLEAN:
return bool(GetPListValue<quint8>(node, plist_get_bool_val));
case PLIST_UINT:
return QVariant::fromValue(GetPListValue<uint64_t>(node, plist_get_uint_val));
case PLIST_STRING: {
char* data = GetPListValue<char*>(node, plist_get_string_val);
QString ret = QString::fromUtf8(data);
free(data);
return ret;
}
default:
qLog(Warning) << "Unhandled PList type";
return QVariant();
}
}
QStringList iMobileDeviceConnection::ReadDirectory(const QString& path,
QDir::Filters filters) {
char** list = NULL;
afc_error_t err = afc_read_directory(afc_, path.toUtf8().constData(), &list);
if (err != AFC_E_SUCCESS || !list) {
return QStringList();
}
QStringList ret;
for (char** p = list ; *p != NULL ; ++p) {
QString filename = QString::fromUtf8(*p);
free(*p);
if (filters == QDir::NoFilter)
ret << filename;
else {
if (filters & QDir::NoDotAndDotDot && (filename == "." || filename == ".."))
continue;
if (!(filters & QDir::Hidden) && filename.startsWith("."))
continue;
QString filetype = GetFileInfo(path + "/" + filename, "st_ifmt");
if ((filetype == "S_IFREG" && (filters & QDir::Files)) ||
(filetype == "S_IFDIR" && (filters & QDir::Dirs)) ||
(filetype == "S_IFLNK" && (!(filters & QDir::NoSymLinks))))
ret << filename;
}
}
free(list);
return ret;
}
bool iMobileDeviceConnection::MkDir(const QString& path) {
afc_error_t err = afc_make_directory(afc_, path.toUtf8().constData());
return err == AFC_E_SUCCESS;
}
QString iMobileDeviceConnection::GetFileInfo(const QString& path, const QString& key) {
QString ret;
char** infolist = NULL;
afc_error_t err = afc_get_file_info(afc_, path.toUtf8().constData(), &infolist);
if (err != AFC_E_SUCCESS || !infolist) {
return ret;
}
QString last_key;
for (char** p = infolist ; *p != NULL ; ++p) {
if (last_key.isNull()) {
last_key = QString::fromUtf8(*p);
} else {
if (last_key == key)
ret = QString::fromUtf8(*p);
last_key = QString();
}
free(*p);
}
free(infolist);
return ret;
}
bool iMobileDeviceConnection::Exists(const QString& path) {
return !GetFileInfo(path, "st_ifmt").isNull();
}
QString iMobileDeviceConnection::GetUnusedFilename(
Itdb_iTunesDB* itdb, const Song& metadata) {
// This function does the same as itdb_cp_get_dest_filename, except it
// accesses the device's filesystem through imobiledevice.
// Get the total number of F.. directories
int total_musicdirs = 0;
for ( ; ; ++total_musicdirs) {
QString dir;
dir.sprintf("/iTunes_Control/Music/F%02d", total_musicdirs);
if (!Exists(dir))
break;
}
if (total_musicdirs <= 0) {
qLog(Warning) << "No 'F..'' directories found on iPod";
return QString();
}
// Pick one at random
const int dir_num = qrand() % total_musicdirs;
QString dir;
dir.sprintf("/iTunes_Control/Music/F%02d", dir_num);
if (!Exists(dir)) {
qLog(Warning) << "Music directory doesn't exist:" << dir;
return QString();
}
// Use the same file extension as the original file, default to mp3.
QString extension = metadata.url().path().section('.', -1, -1).toLower();
if (extension.isEmpty())
extension = "mp3";
// Loop until we find an unused filename.
// Use the same naming convention as libgpod, which is
// "libgpod" + 6-digit random number
static const int kRandMax = 999999;
QString filename;
forever {
filename.sprintf("libgpod%06d", qrand() % kRandMax);
filename += "." + extension;
if (!Exists(dir + "/" + filename))
break;
}
return dir + "/" + filename;
}

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 IMOBILEDEVICECONNECTION_H
#define IMOBILEDEVICECONNECTION_H
#include "core/song.h"
#include <libimobiledevice/afc.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <gpod/itdb.h>
#include <QDir>
#include <QStringList>
#include <QVariant>
class iMobileDeviceConnection {
public:
iMobileDeviceConnection(const QString& uuid);
~iMobileDeviceConnection();
afc_client_t afc() { return afc_; }
QVariant GetProperty(const QString& property, const QString& domain = QString());
QStringList ReadDirectory(const QString& path, QDir::Filters filters = QDir::NoFilter);
bool MkDir(const QString& path);
QString GetFileInfo(const QString& path, const QString& key);
bool Exists(const QString& path);
QString GetUnusedFilename(Itdb_iTunesDB* itdb, const Song& metadata);
private:
Q_DISABLE_COPY(iMobileDeviceConnection);
idevice_t device_;
afc_client_t afc_;
uint16_t afc_port_;
};
#endif // IMOBILEDEVICECONNECTION_H

View File

@ -26,10 +26,6 @@
#include "core/taskmanager.h"
#include "core/utilities.h"
#ifdef HAVE_IMOBILEDEVICE
# include "gst/afcsrc/gstafcsrc.h"
#endif
#ifdef HAVE_MOODBAR
# include "gst/moodbar/spectrum.h"
#endif
@ -118,10 +114,6 @@ bool GstEngine::Init() {
void GstEngine::InitialiseGstreamer() {
gst_init(NULL, NULL);
#ifdef HAVE_IMOBILEDEVICE
afcsrc_register_static();
#endif
#ifdef HAVE_MOODBAR
gstmoodbar_register_static();
#endif