diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d1ffd02cb..abfb19f178 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -220,6 +220,7 @@ set(mirall_SRCS mirall/ignorelisteditor.cpp mirall/itemprogressdialog.cpp mirall/owncloudgui.cpp + mirall/socketapi.cpp ) set(mirall_HEADERS @@ -245,6 +246,7 @@ set(mirall_HEADERS mirall/ignorelisteditor.h mirall/itemprogressdialog.h mirall/owncloudgui.h + mirall/socketapi.h ) if( UNIX AND NOT APPLE) diff --git a/src/mirall/application.cpp b/src/mirall/application.cpp index 61c20cbe4c..de19069d7c 100644 --- a/src/mirall/application.cpp +++ b/src/mirall/application.cpp @@ -33,6 +33,7 @@ #include "mirall/logger.h" #include "mirall/utility.h" #include "mirall/connectionvalidator.h" +#include "mirall/socketapi.h" #include "creds/abstractcredentials.h" @@ -146,6 +147,9 @@ Application::Application(int &argc, char **argv) : connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup())); qDebug() << "Network Location: " << NetworkLocation::currentLocation().encoded(); + + + _socketApi = new SocketApi(this, cfg.configPathWithAppName().append(QLatin1String("socket"))); } Application::~Application() @@ -159,7 +163,6 @@ void Application::slotCleanup() // that saving the geometries happens ASAP during a OS shutdown _gui->slotShutdown(); _gui->deleteLater(); - } void Application::slotStartUpdateDetector() diff --git a/src/mirall/application.h b/src/mirall/application.h index 80025fbe6a..a45f5c59e8 100644 --- a/src/mirall/application.h +++ b/src/mirall/application.h @@ -44,6 +44,7 @@ class ownCloudInfo; class SslErrorDialog; class SettingsDialog; class ItemProgressDialog; +class SocketApi; class Application : public SharedTools::QtSingleApplication { @@ -91,6 +92,7 @@ private: void runValidator(); QPointer _gui; + QPointer _socketApi; // QNetworkConfigurationManager *_networkMgr; SslErrorDialog *_sslErrorDialog; diff --git a/src/mirall/folderman.cpp b/src/mirall/folderman.cpp index 7e7ca9d2d0..76f4533c77 100644 --- a/src/mirall/folderman.cpp +++ b/src/mirall/folderman.cpp @@ -429,6 +429,23 @@ void FolderMan::addFolderDefinition(const QString& alias, const QString& sourceF settings.sync(); } +Folder *FolderMan::folderForPath(const QUrl &path) +{ + QString absolutePath = path.toLocalFile(); + absolutePath.append("/"); + + foreach(Folder* folder, map().values()) + { + if(absolutePath.startsWith(folder->path())) + { + qDebug() << "found folder: " << folder->path() << " for " << absolutePath; + return folder; + } + } + + return 0; +} + void FolderMan::removeAllFolderDefinitions() { foreach( Folder *f, _folderMap.values() ) { diff --git a/src/mirall/folderman.h b/src/mirall/folderman.h index 8051d0c469..50a0e9b5a2 100644 --- a/src/mirall/folderman.h +++ b/src/mirall/folderman.h @@ -50,6 +50,9 @@ public: */ void addFolderDefinition(const QString&, const QString&, const QString& ); + /** Returns the folder which the file or directory stored in path is in */ + Folder* folderForPath(const QUrl& path); + /** Returns the folder by alias or NULL if no folder with the alias exists. */ Folder *folder( const QString& ); diff --git a/src/mirall/mirallconfigfile.cpp b/src/mirall/mirallconfigfile.cpp index 6b18fcb0a0..8832a0b09a 100644 --- a/src/mirall/mirallconfigfile.cpp +++ b/src/mirall/mirallconfigfile.cpp @@ -170,6 +170,12 @@ QString MirallConfigFile::configPath() const return dir; } +QString MirallConfigFile::configPathWithAppName() const +{ + //HACK + return QFileInfo( configFile() ).dir().absolutePath().append("/"); +} + QString MirallConfigFile::excludeFile(Scope scope) const { // prefer sync-exclude.lst, but if it does not exist, check for diff --git a/src/mirall/mirallconfigfile.h b/src/mirall/mirallconfigfile.h index 225905567b..aeccd4f4da 100644 --- a/src/mirall/mirallconfigfile.h +++ b/src/mirall/mirallconfigfile.h @@ -33,6 +33,7 @@ public: enum Scope { UserScope, SystemScope }; QString configPath() const; + QString configPathWithAppName() const; QString configFile() const; QString excludeFile(Scope scope) const; diff --git a/src/mirall/socketapi.cpp b/src/mirall/socketapi.cpp new file mode 100644 index 0000000000..2a4193b5d5 --- /dev/null +++ b/src/mirall/socketapi.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) by Dominik Schmidt + * + * 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. + */ + + +#include "socketapi.h" + +#include "mirall/mirallconfigfile.h" +#include "mirall/folderman.h" +#include "mirall/folder.h" +#include "mirall/utility.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Mirall { + +#define DEBUG qDebug() << "SocketApi: " + +SocketApi::SocketApi(QObject* parent, const QUrl& localFile) + : QObject(parent) + , _localServer(0) +{ + QString socketPath = localFile.toLocalFile(); + DEBUG << "ctor: " << socketPath; + + // setup socket + _localServer = new QLocalServer(this); + QLocalServer::removeServer(socketPath); + if(!_localServer->listen(socketPath)) + DEBUG << "can't start server"; + else + DEBUG << "server started"; + connect(_localServer, SIGNAL(newConnection()), this, SLOT(onNewConnection())); + + // folder watcher + connect(FolderMan::instance(), SIGNAL(folderSyncStateChange(QString)), SLOT(onSyncStateChanged(QString))); +} + +SocketApi::~SocketApi() +{ + DEBUG << "dtor"; + _localServer->close(); +} + +void SocketApi::onNewConnection() +{ + QLocalSocket* socket = _localServer->nextPendingConnection(); + DEBUG << "New connection " << socket; + connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(socket, SIGNAL(disconnected()), this, SLOT(onLostConnection())); + Q_ASSERT(socket->readAll().isEmpty()); + + _listeners.append(socket); +} + +void SocketApi::onLostConnection() +{ + DEBUG << "Lost connection " << sender(); + + QLocalSocket* socket = qobject_cast< QLocalSocket* >(sender()); + _listeners.removeAll(socket); +} + + +void SocketApi::onReadyRead() +{ + QLocalSocket* socket = qobject_cast(sender()); + Q_ASSERT(socket); + + while(socket->canReadLine()) + { + QString line = QString::fromUtf8(socket->readLine()).trimmed(); + QString command = line.split(":").first(); + QString function = QString(QLatin1String("command_")).append(command); + + QString functionWithArguments = function + QLatin1String("(QString,QLocalSocket*)"); + int indexOfMethod = this->metaObject()->indexOfMethod(functionWithArguments.toAscii()); + + QString argument = line.remove(0, command.length()+1).trimmed(); + if(indexOfMethod != -1) + { + QMetaObject::invokeMethod(this, function.toAscii(), Q_ARG(QString, argument), Q_ARG(QLocalSocket*, socket)); + } + else + { + DEBUG << "The command is not supported by this version of the client:" << command << "with argument:" << argument; + } + } +} + +void SocketApi::onSyncStateChanged(const QString&) +{ + DEBUG << "Sync state changed"; + + broadcastMessage("UPDATE_VIEW"); +} + + +void SocketApi::sendMessage(QLocalSocket* socket, const QString& message) +{ + DEBUG << "Sending message: " << message; + QString localMessage = message; + socket->write(localMessage.append("\n").toUtf8()); +} + +void SocketApi::broadcastMessage(const QString& message) +{ + DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << message; + foreach(QLocalSocket* current, _listeners) + { + sendMessage(current, message); + } +} + +void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, QLocalSocket* socket) +{ + qDebug() << Q_FUNC_INFO << argument; + //TODO: do security checks?! + Folder* folder = FolderMan::instance()->folderForPath( argument ); + // this can happen in offline mode e.g.: nothing to worry about + if(!folder) + { + DEBUG << "folder offline or not watched:" << argument; + return; + } + + QDir dir(argument); + foreach(QString entry, dir.entryList(QDir::AllEntries|QDir::NoDotAndDotDot)) + { + QString absoluteFilePath = dir.absoluteFilePath(entry); + QString statusString; + SyncFileStatus fileStatus = folder->fileStatus(absoluteFilePath.mid(folder->path().length())); + switch(fileStatus) + { + case STATUS_NONE: + statusString = QLatin1String("STATUS_NONE"); + break; + case STATUS_EVAL: + statusString = QLatin1String("STATUS_EVAL"); + break; + case STATUS_REMOVE: + statusString = QLatin1String("STATUS_REMOVE"); + break; + case STATUS_RENAME: + statusString = QLatin1String("STATUS_RENAME"); + break; + case STATUS_NEW: + statusString = QLatin1String("STATUS_NEW"); + break; + case STATUS_CONFLICT: + statusString = QLatin1String("STATUS_CONFLICT"); + break; + case STATUS_IGNORE: + statusString = QLatin1String("STATUS_IGNORE"); + break; + case STATUS_SYNC: + statusString = QLatin1String("STATUS_SYNC"); + break; + case STATUS_STAT_ERROR: + statusString = QLatin1String("STATUS_STAT_ERROR"); + break; + case STATUS_ERROR: + statusString = QLatin1String("STATUS_ERROR"); + break; + case STATUS_UPDATED: + statusString = QLatin1String("STATUS_UPDATED"); + break; + default: + qWarning() << "not all SyncFileStatus items checked!"; + Q_ASSERT(false); + statusString = QLatin1String("STATUS_NONE"); + + } + QString message("%1:%2:%3"); + message = message.arg("STATUS").arg(statusString).arg(absoluteFilePath); + sendMessage(socket, message); + } +} + +} // namespace Mirall diff --git a/src/mirall/socketapi.h b/src/mirall/socketapi.h new file mode 100644 index 0000000000..b0752abfd9 --- /dev/null +++ b/src/mirall/socketapi.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) by Dominik Schmidt + * + * 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. + */ + + +#ifndef SOCKETAPI_H +#define SOCKETAPI_H + +#include + +class QUrl; +class QLocalSocket; +class QLocalServer; +class QStringList; + +namespace Mirall { + +class SocketApi : public QObject +{ +Q_OBJECT + +public: + SocketApi(QObject* parent, const QUrl& localFile); + virtual ~SocketApi(); + +private slots: + void onNewConnection(); + void onLostConnection(); + void onReadyRead(); + void onSyncStateChanged(const QString&); + +private: + void sendMessage(QLocalSocket* socket, const QString& message); + void broadcastMessage(const QString& message); + + Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, QLocalSocket* socket); + +private: + QLocalServer* _localServer; + QList< QLocalSocket* > _listeners; +}; + +} +#endif // SOCKETAPI_H