739 lines
22 KiB
C++
739 lines
22 KiB
C++
/*
|
|
* Cantata
|
|
*
|
|
* Copyright (c) 2011-2014 Craig Drummond <craig.p.drummond@gmail.com>
|
|
*
|
|
* ----
|
|
*
|
|
* This program 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,f
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "remotefsdevice.h"
|
|
#include "config.h"
|
|
#include "remotedevicepropertiesdialog.h"
|
|
#include "devicepropertieswidget.h"
|
|
#include "actiondialog.h"
|
|
#include "inputdialog.h"
|
|
#include "utils.h"
|
|
#include "httpserver.h"
|
|
#include "localize.h"
|
|
#include "settings.h"
|
|
#include "mountpoints.h"
|
|
#include "mounterinterface.h"
|
|
#include "avahi/avahi.h"
|
|
#include "avahi/avahiservice.h"
|
|
#include <QDBusConnection>
|
|
#include <QTimer>
|
|
#include <QProcess>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
#include <kmountpoint.h>
|
|
#include <kde_file.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
const QLatin1String RemoteFsDevice::constPromptPassword("-");
|
|
const QLatin1String RemoteFsDevice::constSshfsProtocol("sshfs");
|
|
const QLatin1String RemoteFsDevice::constFileProtocol("file");
|
|
const QLatin1String RemoteFsDevice::constDomainQuery("domain");
|
|
const QLatin1String RemoteFsDevice::constSambaProtocol("smb");
|
|
const QLatin1String RemoteFsDevice::constServiceNameQuery("name");
|
|
const QLatin1String RemoteFsDevice::constSambaAvahiProtocol("smb-avahi");
|
|
|
|
static const QLatin1String constCfgPrefix("RemoteFsDevice-");
|
|
static const QLatin1String constCfgKey("remoteFsDevices");
|
|
|
|
static QString mountPoint(const RemoteFsDevice::Details &details, bool create)
|
|
{
|
|
if (details.isLocalFile()) {
|
|
return details.url.path();
|
|
}
|
|
return Utils::cacheDir(QLatin1String("mount/")+details.name, create);
|
|
}
|
|
|
|
void RemoteFsDevice::Details::load(const QString &group)
|
|
{
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KConfigGroup cfg(KGlobal::config(), constCfgPrefix+group);
|
|
#else
|
|
QSettings cfg;
|
|
cfg.beginGroup(constCfgPrefix+group);
|
|
#endif
|
|
name=group;
|
|
QString u=GET_STRING("url", QString());
|
|
|
|
if (u.isEmpty()) {
|
|
QString protocol=GET_STRING("protocol", QString());
|
|
QString host=GET_STRING("host", QString());
|
|
QString user=GET_STRING("user", QString());
|
|
QString path=GET_STRING("path", QString());
|
|
int port=GET_INT("port", 0);
|
|
u=(protocol.isEmpty() ? "file" : protocol+"://")+user+(!user.isEmpty() && !host.isEmpty() ? "@" : "")+host+(0==port ? "" : (":"+QString::number(port)))+path;
|
|
}
|
|
if (!u.isEmpty()) {
|
|
url=QUrl(u);
|
|
}
|
|
extraOptions=GET_STRING("extraOptions", QString());
|
|
configured=GET_BOOL("configured", configured);
|
|
}
|
|
|
|
void RemoteFsDevice::Details::save() const
|
|
{
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KConfigGroup cfg(KGlobal::config(), constCfgPrefix+name);
|
|
#else
|
|
QSettings cfg;
|
|
cfg.beginGroup(constCfgPrefix+name);
|
|
#endif
|
|
SET_VALUE("url", url.toString());
|
|
SET_VALUE("extraOptions", extraOptions);
|
|
SET_VALUE("configured", configured);
|
|
CFG_SYNC;
|
|
}
|
|
|
|
static inline bool isValid(const RemoteFsDevice::Details &d)
|
|
{
|
|
return d.isLocalFile() || RemoteFsDevice::constSshfsProtocol==d.url.scheme() ||
|
|
RemoteFsDevice::constSambaProtocol==d.url.scheme() || RemoteFsDevice::constSambaAvahiProtocol==d.url.scheme();
|
|
}
|
|
|
|
static inline bool isMountable(const RemoteFsDevice::Details &d)
|
|
{
|
|
return RemoteFsDevice::constSshfsProtocol==d.url.scheme() ||
|
|
RemoteFsDevice::constSambaProtocol==d.url.scheme() || RemoteFsDevice::constSambaAvahiProtocol==d.url.scheme();
|
|
}
|
|
|
|
QList<Device *> RemoteFsDevice::loadAll(MusicModel *m)
|
|
{
|
|
QList<Device *> devices;
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KConfigGroup cfg(KGlobal::config(), "General");
|
|
#else
|
|
QSettings cfg;
|
|
#endif
|
|
QStringList names=GET_STRINGLIST(constCfgKey, QStringList());
|
|
foreach (const QString &n, names) {
|
|
Details d;
|
|
d.load(n);
|
|
if (d.isEmpty()) {
|
|
REMOVE_GROUP(constCfgPrefix+n);
|
|
} else if (isValid(d)) {
|
|
devices.append(new RemoteFsDevice(m, d));
|
|
}
|
|
}
|
|
if (devices.count()!=names.count()) {
|
|
CFG_SYNC;
|
|
}
|
|
return devices;
|
|
}
|
|
|
|
Device * RemoteFsDevice::create(MusicModel *m, const DeviceOptions &options, const Details &d)
|
|
{
|
|
if (d.isEmpty()) {
|
|
return 0;
|
|
}
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KConfigGroup cfg(KGlobal::config(), "General");
|
|
#else
|
|
QSettings cfg;
|
|
#endif
|
|
QStringList names=GET_STRINGLIST(constCfgKey, QStringList());
|
|
if (names.contains(d.name)) {
|
|
return 0;
|
|
}
|
|
names.append(d.name);
|
|
SET_VALUE(constCfgKey, names);
|
|
d.save();
|
|
if (isValid(d)) {
|
|
return new RemoteFsDevice(m, options, d);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RemoteFsDevice::destroy(bool removeFromConfig)
|
|
{
|
|
if (removeFromConfig) {
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KConfigGroup cfg(KGlobal::config(), "General");
|
|
#else
|
|
QSettings cfg;
|
|
#endif
|
|
QStringList names=GET_STRINGLIST(constCfgKey, QStringList());
|
|
if (names.contains(details.name)) {
|
|
names.removeAll(details.name);
|
|
REMOVE_GROUP(id());
|
|
SET_VALUE(constCfgKey, names);
|
|
CFG_SYNC;
|
|
}
|
|
}
|
|
stopScanner();
|
|
if (isConnected()) {
|
|
unmount();
|
|
}
|
|
if (isMountable(details)) {
|
|
QString mp=mountPoint(details, false);
|
|
if (!mp.isEmpty()) {
|
|
QDir d(mp);
|
|
if (d.exists()) {
|
|
d.rmdir(mp);
|
|
}
|
|
}
|
|
}
|
|
deleteLater();
|
|
}
|
|
|
|
void RemoteFsDevice::serviceAdded(const QString &name)
|
|
{
|
|
if (name==details.serviceName && constSambaAvahiProtocol==details.url.scheme()) {
|
|
sub=i18n("Available");
|
|
updateStatus();
|
|
}
|
|
}
|
|
|
|
void RemoteFsDevice::serviceRemoved(const QString &name)
|
|
{
|
|
if (name==details.serviceName && constSambaAvahiProtocol==details.url.scheme()) {
|
|
sub=i18n("Not Available");
|
|
updateStatus();
|
|
}
|
|
}
|
|
|
|
QString RemoteFsDevice::createUdi(const QString &n)
|
|
{
|
|
return constCfgPrefix+n;
|
|
}
|
|
|
|
void RemoteFsDevice::renamed(const QString &oldName, const QString &newName)
|
|
{
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KConfigGroup cfg(KGlobal::config(), "General");
|
|
#else
|
|
QSettings cfg;
|
|
#endif
|
|
QStringList names=GET_STRINGLIST(constCfgKey, QStringList());
|
|
if (names.contains(oldName)) {
|
|
names.removeAll(oldName);
|
|
REMOVE_GROUP(createUdi(oldName));
|
|
}
|
|
if (!names.contains(newName)) {
|
|
names.append(newName);
|
|
}
|
|
SET_VALUE(constCfgKey, names);
|
|
CFG_SYNC;
|
|
}
|
|
|
|
RemoteFsDevice::RemoteFsDevice(MusicModel *m, const DeviceOptions &options, const Details &d)
|
|
: FsDevice(m, d.name, createUdi(d.name))
|
|
, mountToken(0)
|
|
, currentMountStatus(false)
|
|
, details(d)
|
|
, proc(0)
|
|
, mounterIface(0)
|
|
, messageSent(false)
|
|
{
|
|
opts=options;
|
|
// details.path=Utils::fixPath(details.path);
|
|
load();
|
|
mount();
|
|
icn=Icon(details.isLocalFile() ? "inode-directory" : (constSshfsProtocol==details.url.scheme() ? "utilities-terminal" : "network-server"));
|
|
}
|
|
|
|
RemoteFsDevice::RemoteFsDevice(MusicModel *m, const Details &d)
|
|
: FsDevice(m, d.name, createUdi(d.name))
|
|
, mountToken(0)
|
|
, currentMountStatus(false)
|
|
, details(d)
|
|
, proc(0)
|
|
, mounterIface(0)
|
|
, messageSent(false)
|
|
{
|
|
// details.path=Utils::fixPath(details.path);
|
|
setup();
|
|
icn=Icon(details.isLocalFile() ? "inode-directory" : (constSshfsProtocol==details.url.scheme() ? "utilities-terminal" : "network-server"));
|
|
}
|
|
|
|
RemoteFsDevice::~RemoteFsDevice() {
|
|
}
|
|
|
|
void RemoteFsDevice::toggle()
|
|
{
|
|
if (isConnected()) {
|
|
stopScanner();
|
|
unmount();
|
|
} else {
|
|
mount();
|
|
}
|
|
}
|
|
|
|
ComGooglecodeCantataMounterInterface * RemoteFsDevice::mounter()
|
|
{
|
|
if (!mounterIface) {
|
|
if (!QDBusConnection::systemBus().interface()->isServiceRegistered(ComGooglecodeCantataMounterInterface::staticInterfaceName())) {
|
|
QDBusConnection::systemBus().interface()->startService(ComGooglecodeCantataMounterInterface::staticInterfaceName());
|
|
}
|
|
mounterIface=new ComGooglecodeCantataMounterInterface(ComGooglecodeCantataMounterInterface::staticInterfaceName(),
|
|
"/Mounter", QDBusConnection::systemBus(), this);
|
|
connect(mounterIface, SIGNAL(mountStatus(const QString &, int, int)), SLOT(mountStatus(const QString &, int, int)));
|
|
connect(mounterIface, SIGNAL(umountStatus(const QString &, int, int)), SLOT(umountStatus(const QString &, int, int)));
|
|
}
|
|
return mounterIface;
|
|
}
|
|
|
|
void RemoteFsDevice::mount()
|
|
{
|
|
if (details.isLocalFile()) {
|
|
return;
|
|
}
|
|
if (isConnected() || proc) {
|
|
return;
|
|
}
|
|
|
|
if (messageSent) {
|
|
return;
|
|
}
|
|
if (constSambaAvahiProtocol==details.url.scheme()) {
|
|
Details det=details;
|
|
AvahiService *srv=Avahi::self()->getService(det.serviceName);
|
|
if (!srv || srv->getHost().isEmpty() || 0==srv->getPort()) {
|
|
emit error(i18n("Failed to resolve connection details for %1", details.name));
|
|
return;
|
|
}
|
|
if (constPromptPassword==det.url.password()) {
|
|
bool ok=false;
|
|
QString passwd=InputDialog::getPassword(QString(), &ok, QApplication::activeWindow());
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
det.url.setPassword(passwd);
|
|
}
|
|
det.url.setScheme(constSambaProtocol);
|
|
det.url.setHost(srv->getHost());
|
|
det.url.setPort(srv->getPort());
|
|
mounter()->mount(det.url.toString(), mountPoint(details, true), getuid(), getgid(), getpid());
|
|
setStatusMessage(i18n("Connecting..."));
|
|
messageSent=true;
|
|
return;
|
|
}
|
|
if (constSambaProtocol==details.url.scheme()) {
|
|
Details det=details;
|
|
if (constPromptPassword==det.url.password()) {
|
|
bool ok=false;
|
|
QString passwd=InputDialog::getPassword(QString(), &ok, QApplication::activeWindow());
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
det.url.setPassword(passwd);
|
|
}
|
|
mounter()->mount(det.url.toString(), mountPoint(details, true), getuid(), getgid(), getpid());
|
|
setStatusMessage(i18n("Connecting..."));
|
|
messageSent=true;
|
|
return;
|
|
}
|
|
|
|
QString cmd;
|
|
QStringList args;
|
|
if (!details.isLocalFile() && !details.isEmpty()) {
|
|
// If user has added 'IdentityFile' to extra options, then no password prompting is required...
|
|
bool needAskPass=!details.extraOptions.contains("IdentityFile=");
|
|
|
|
if (needAskPass) {
|
|
if (ttyname(0)) {
|
|
emit error(i18n("Password prompting does not work when cantata is started from the commandline."));
|
|
return;
|
|
}
|
|
QStringList askPassList;
|
|
if (Utils::KDE==Utils::currentDe()) {
|
|
askPassList << QLatin1String("ksshaskpass") << QLatin1String("ssh-askpass") << QLatin1String("ssh-askpass-gnome");
|
|
} else {
|
|
askPassList << QLatin1String("ssh-askpass-gnome") << QLatin1String("ssh-askpass") << QLatin1String("ksshaskpass");
|
|
}
|
|
|
|
QString askPass;
|
|
foreach (const QString &ap, askPassList) {
|
|
askPass=Utils::findExe(ap);
|
|
if (!askPass.isEmpty()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (askPass.isEmpty()) {
|
|
emit error(i18n("No suitable ssh-askpass application installed! This is required for entering passwords."));
|
|
return;
|
|
}
|
|
}
|
|
cmd=Utils::findExe("sshfs");
|
|
if (!cmd.isEmpty()) {
|
|
if (!QDir(mountPoint(details, true)).entryList(QDir::NoDot|QDir::NoDotDot|QDir::AllEntries|QDir::Hidden).isEmpty()) {
|
|
emit error(i18n("Mount point (\"%1\") is not empty!", mountPoint(details, true)));
|
|
return;
|
|
}
|
|
|
|
args << details.url.userName()+QChar('@')+details.url.host()+QChar(':')+details.url.path()<< QLatin1String("-p")
|
|
<< QString::number(details.url.port()) << mountPoint(details, true)
|
|
<< QLatin1String("-o") << QLatin1String("ServerAliveInterval=15");
|
|
//<< QLatin1String("-o") << QLatin1String("Ciphers=arcfour");
|
|
if (!details.extraOptions.isEmpty()) {
|
|
args << details.extraOptions.split(' ', QString::SkipEmptyParts);
|
|
}
|
|
} else {
|
|
emit error(i18n("\"sshfs\" is not installed!"));
|
|
}
|
|
}
|
|
|
|
if (!cmd.isEmpty()) {
|
|
setStatusMessage(i18n("Connecting..."));
|
|
proc=new QProcess(this);
|
|
proc->setProperty("mount", true);
|
|
connect(proc, SIGNAL(finished(int)), SLOT(procFinished(int)));
|
|
proc->start(cmd, args, QIODevice::ReadOnly);
|
|
}
|
|
}
|
|
|
|
void RemoteFsDevice::unmount()
|
|
{
|
|
if (details.isLocalFile()) {
|
|
return;
|
|
}
|
|
|
|
if (!isConnected() || proc) {
|
|
return;
|
|
}
|
|
|
|
if (messageSent) {
|
|
return;
|
|
}
|
|
if (constSambaProtocol==details.url.scheme() || constSambaAvahiProtocol==details.url.scheme()) {
|
|
mounter()->umount(mountPoint(details, false), getpid());
|
|
setStatusMessage(i18n("Disconnecting..."));
|
|
messageSent=true;
|
|
return;
|
|
}
|
|
|
|
QString cmd;
|
|
QStringList args;
|
|
if (!details.isLocalFile()) {
|
|
QString mp=mountPoint(details, false);
|
|
if (!mp.isEmpty()) {
|
|
cmd=Utils::findExe("fusermount");
|
|
if (!cmd.isEmpty()) {
|
|
args << QLatin1String("-u") << QLatin1String("-z") << mp;
|
|
} else {
|
|
emit error(i18n("\"fusermount\" is not installed!"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cmd.isEmpty()) {
|
|
setStatusMessage(i18n("Disconnecting..."));
|
|
proc=new QProcess(this);
|
|
proc->setProperty("unmount", true);
|
|
connect(proc, SIGNAL(finished(int)), SLOT(procFinished(int)));
|
|
proc->start(cmd, args, QIODevice::ReadOnly);
|
|
}
|
|
}
|
|
|
|
void RemoteFsDevice::procFinished(int exitCode)
|
|
{
|
|
bool wasMount=proc->property("mount").isValid();
|
|
proc->deleteLater();
|
|
proc=0;
|
|
|
|
if (0!=exitCode) {
|
|
emit error(wasMount ? i18n("Failed to connect to \"%1\"", details.name)
|
|
: i18n("Failed to disconnect from \"%1\"", details.name));
|
|
setStatusMessage(QString());
|
|
} else if (wasMount) {
|
|
setStatusMessage(i18n("Updating tracks..."));
|
|
load();
|
|
emit connectionStateHasChanged(id(), true);
|
|
} else {
|
|
setStatusMessage(QString());
|
|
update=new MusicLibraryItemRoot;
|
|
scanned=false;
|
|
emit updating(id(), false);
|
|
emit connectionStateHasChanged(id(), false);
|
|
}
|
|
}
|
|
|
|
void RemoteFsDevice::mountStatus(const QString &mp, int pid, int st)
|
|
{
|
|
if (pid==getpid() && mp==mountPoint(details, false)) {
|
|
messageSent=false;
|
|
if (0!=st) {
|
|
emit error(i18n("Failed to connect to \"%1\"", details.name));
|
|
setStatusMessage(QString());
|
|
} else {
|
|
setStatusMessage(i18n("Updating tracks..."));
|
|
load();
|
|
emit connectionStateHasChanged(id(), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemoteFsDevice::umountStatus(const QString &mp, int pid, int st)
|
|
{
|
|
if (pid==getpid() && mp==mountPoint(details, false)) {
|
|
messageSent=false;
|
|
if (0!=st) {
|
|
emit error(i18n("Failed to disconnect from \"%1\"", details.name));
|
|
setStatusMessage(QString());
|
|
} else {
|
|
setStatusMessage(QString());
|
|
update=new MusicLibraryItemRoot;
|
|
emit updating(id(), false);
|
|
emit connectionStateHasChanged(id(), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RemoteFsDevice::isConnected() const
|
|
{
|
|
if (details.isLocalFile()) {
|
|
if (QDir(details.url.path()).exists()) {
|
|
return true;
|
|
}
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
if (mountToken==MountPoints::self()->currentToken()) {
|
|
return currentMountStatus;
|
|
}
|
|
QString mp=mountPoint(details, false);
|
|
if (mp.isEmpty()) {
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
if (opts.useCache && !audioFolder.isEmpty() && QFile::exists(cacheFileName())) {
|
|
currentMountStatus=true;
|
|
return true;
|
|
}
|
|
|
|
mountToken=MountPoints::self()->currentToken();
|
|
currentMountStatus=MountPoints::self()->isMounted(mp);
|
|
if (currentMountStatus) {
|
|
return true;
|
|
}
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
bool RemoteFsDevice::isOldSshfs()
|
|
{
|
|
return constSshfsProtocol==details.url.scheme() && 0==spaceInfo.used() && 1073741824000==spaceInfo.size();
|
|
}
|
|
|
|
double RemoteFsDevice::usedCapacity()
|
|
{
|
|
if (cacheProgress>-1) {
|
|
return (cacheProgress*1.0)/100.0;
|
|
}
|
|
if (!isConnected()) {
|
|
return -1.0;
|
|
}
|
|
|
|
spaceInfo.setPath(mountPoint(details, false));
|
|
if (isOldSshfs()) {
|
|
return -1.0;
|
|
}
|
|
return spaceInfo.size()>0 ? (spaceInfo.used()*1.0)/(spaceInfo.size()*1.0) : -1.0;
|
|
}
|
|
|
|
QString RemoteFsDevice::capacityString()
|
|
{
|
|
if (cacheProgress>-1) {
|
|
return statusMessage();
|
|
}
|
|
if (!isConnected()) {
|
|
return i18n("Not Connected");
|
|
}
|
|
|
|
spaceInfo.setPath(mountPoint(details, false));
|
|
if (isOldSshfs()) {
|
|
return i18n("Capacity Unknown");
|
|
}
|
|
return i18n("%1 free", Utils::formatByteSize(spaceInfo.size()-spaceInfo.used()));
|
|
}
|
|
|
|
qint64 RemoteFsDevice::freeSpace()
|
|
{
|
|
if (!isConnected()) { // || !details.isLocalFile()) {
|
|
return 0;
|
|
}
|
|
|
|
spaceInfo.setPath(mountPoint(details, false));
|
|
if (isOldSshfs()) {
|
|
return 0;
|
|
}
|
|
return spaceInfo.size()-spaceInfo.used();
|
|
}
|
|
|
|
void RemoteFsDevice::load()
|
|
{
|
|
if (RemoteFsDevice::constSambaAvahiProtocol==details.url.scheme()) {
|
|
// Start Avahi listener...
|
|
Avahi::self();
|
|
#if QT_VERSION < 0x050000
|
|
if (details.url.hasQueryItem(constServiceNameQuery)) {
|
|
details.serviceName=details.url.queryItemValue(constServiceNameQuery);
|
|
}
|
|
#else // QT_VERSION
|
|
QUrlQuery q(details.url);
|
|
if (q.hasQueryItem(constServiceNameQuery)) {
|
|
details.serviceName=q.queryItemValue(constServiceNameQuery);
|
|
}
|
|
#endif // QT_VERSION
|
|
|
|
if (!details.serviceName.isEmpty()) {
|
|
AvahiService *srv=Avahi::self()->getService(details.serviceName);
|
|
if (!srv || srv->getHost().isEmpty()) {
|
|
sub=i18n("Not Available");
|
|
} else {
|
|
sub=i18n("Available");
|
|
}
|
|
}
|
|
connect(Avahi::self(), SIGNAL(serviceAdded(QString)), SLOT(serviceAdded(QString)));
|
|
connect(Avahi::self(), SIGNAL(serviceRemoved(QString)), SLOT(serviceRemoved(QString)));
|
|
}
|
|
|
|
if (isConnected()) {
|
|
setAudioFolder();
|
|
readOpts(settingsFileName(), opts, true);
|
|
rescan(false); // Read from cache if we have it!
|
|
}
|
|
}
|
|
|
|
void RemoteFsDevice::setup()
|
|
{
|
|
details.load(details.name);
|
|
configured=details.configured;
|
|
if (isConnected()) {
|
|
readOpts(settingsFileName(), opts, true);
|
|
}
|
|
load();
|
|
}
|
|
|
|
void RemoteFsDevice::setAudioFolder() const
|
|
{
|
|
audioFolder=Utils::fixPath(mountPoint(details, true));
|
|
}
|
|
|
|
void RemoteFsDevice::configure(QWidget *parent)
|
|
{
|
|
if (isRefreshing()) {
|
|
return;
|
|
}
|
|
|
|
RemoteDevicePropertiesDialog *dlg=new RemoteDevicePropertiesDialog(parent);
|
|
connect(dlg, SIGNAL(updatedSettings(const DeviceOptions &, const RemoteFsDevice::Details &)),
|
|
SLOT(saveProperties(const DeviceOptions &, const RemoteFsDevice::Details &)));
|
|
if (!configured) {
|
|
connect(dlg, SIGNAL(cancelled()), SLOT(saveProperties()));
|
|
}
|
|
dlg->show(opts, details,
|
|
DevicePropertiesWidget::Prop_All-(DevicePropertiesWidget::Prop_Name+DevicePropertiesWidget::Prop_Folder+DevicePropertiesWidget::Prop_AutoScan),
|
|
false, isConnected());
|
|
}
|
|
|
|
bool RemoteFsDevice::canPlaySongs() const
|
|
{
|
|
return details.isLocalFile() || HttpServer::self()->isAlive();
|
|
}
|
|
|
|
static inline QString toString(bool b)
|
|
{
|
|
return b ? QLatin1String("true") : QLatin1String("false");
|
|
}
|
|
|
|
void RemoteFsDevice::saveOptions()
|
|
{
|
|
opts.save(id());
|
|
}
|
|
|
|
void RemoteFsDevice::saveProperties()
|
|
{
|
|
saveProperties(opts, details);
|
|
}
|
|
|
|
void RemoteFsDevice::saveProperties(const DeviceOptions &newOpts, const Details &nd)
|
|
{
|
|
bool connected=isConnected();
|
|
if (configured && (!connected || opts==newOpts) && (connected || details==nd)) {
|
|
return;
|
|
}
|
|
|
|
bool isLocal=details.isLocalFile();
|
|
|
|
if (connected) {
|
|
if (!configured) {
|
|
details.configured=configured=true;
|
|
details.save();
|
|
}
|
|
if (opts.useCache!=newOpts.useCache) {
|
|
if (opts.useCache) {
|
|
saveCache();
|
|
} else if (opts.useCache && !newOpts.useCache) {
|
|
removeCache();
|
|
}
|
|
}
|
|
opts=newOpts;
|
|
writeOpts(settingsFileName(), opts, true);
|
|
}
|
|
if (!connected || isLocal) {
|
|
Details newDetails=nd;
|
|
Details oldDetails=details;
|
|
bool newName=!oldDetails.name.isEmpty() && oldDetails.name!=newDetails.name;
|
|
bool newDir=oldDetails.url.path()!=newDetails.url.path();
|
|
|
|
if (isLocal && newDir && opts.useCache) {
|
|
removeCache();
|
|
}
|
|
details=newDetails;
|
|
details.configured=configured=true;
|
|
details.save();
|
|
|
|
if (newName) {
|
|
if (!details.isLocalFile()) {
|
|
QString oldMount=mountPoint(oldDetails, false);
|
|
if (!oldMount.isEmpty() && QDir(oldMount).exists()) {
|
|
::rmdir(QFile::encodeName(oldMount).constData());
|
|
}
|
|
}
|
|
setData(details.name);
|
|
renamed(oldDetails.name, details.name);
|
|
deviceId=createUdi(details.name);
|
|
emit udiChanged();
|
|
m_itemData=details.name;
|
|
setStatusMessage(QString());
|
|
}
|
|
if (isLocal && newDir && scanned) {
|
|
rescan(true);
|
|
}
|
|
}
|
|
emit configurationChanged();
|
|
}
|
|
|
|
QString RemoteFsDevice::settingsFileName() const
|
|
{
|
|
if (audioFolder.isEmpty()) {
|
|
setAudioFolder();
|
|
}
|
|
return audioFolder+constCantataSettingsFile;
|
|
}
|