summaryrefslogtreecommitdiff
path: root/distfoldd/serveragent.cc
diff options
context:
space:
mode:
Diffstat (limited to 'distfoldd/serveragent.cc')
-rw-r--r--distfoldd/serveragent.cc225
1 files changed, 225 insertions, 0 deletions
diff --git a/distfoldd/serveragent.cc b/distfoldd/serveragent.cc
new file mode 100644
index 0000000..13e7848
--- /dev/null
+++ b/distfoldd/serveragent.cc
@@ -0,0 +1,225 @@
+#include <QtCore/QDebug>
+
+#include "serveragent.h"
+
+ServerAgent::ServerAgent(QSslSocket *socket, const QDir& local_dir, SyncFlags flags, QObject *parent) :
+ Agent(socket, local_dir, flags, parent)
+{
+ qDebug() << "Starting server agent at" << QDateTime::currentDateTime();
+}
+
+void ServerAgent::handleMessage(MessageType msg, const QByteArray& data)
+{
+ qDebug() << "Server::handleMessage" << msg << data.size();
+ switch (msg) {
+ case MSG_HELLO:
+ sendMessage(MSG_HELLO_REPLY);
+ break;
+ case MSG_FILE_LIST:
+ handleClientFileList(decodeFileInfoList(data));
+ break;
+ case MSG_PULL_FILE:
+ handlePullFile(decodeFileName(data));
+ break;
+ case MSG_PUSH_FILE:
+ handlePushedFile(data);
+ break;
+ case MSG_PUSH_FILE_METADATA:
+ handlePushedMetadata(decodeFileInfoList(data));
+ break;
+ case MSG_DELETE_FILE:
+ handleDeleteFile(decodeFileName(data));
+ break;
+ case MSG_BYE:
+ qDebug() << "Got Bye";
+ emit finished();
+ _socket->close();
+ break;
+ default:
+ qWarning() << "Unknown message";
+ break;
+ }
+}
+
+void ServerAgent::handleClientFileList(const RemoteFileInfoList& list)
+{
+ QFileInfoList files = scanFiles(QDir(wireToLocalPath(_subPath)));
+ ActionInfoList actions_create_dirs, actions_with_files, actions_remove_dirs, actions_update_dirs;
+ QHash<QString, QFileInfo> local_files;
+ QHash<QString, RemoteFileInfo> remote_files;
+ QSet<QString> new_local_files;
+
+ foreach (const QFileInfo& file, files) {
+ const QString wire_path = localToWirePath(file.absoluteFilePath());
+ local_files[wire_path] = file;
+ new_local_files.insert(wire_path);
+ }
+ foreach (const RemoteFileInfo& file, list) {
+ remote_files[file.name()] = file;
+ }
+
+ foreach (const RemoteFileInfo& remote, list) {
+ // Main synchronization logic goes here
+ QString wire_path = remote.name();
+ qDebug() << "Server Handling file" << wire_path;
+ new_local_files.remove(wire_path);
+ if (local_files.contains(wire_path)) {
+ // Both remote and local have the file
+ QFileInfo local = local_files[wire_path];
+ QString parent_name = wireParentPath(wire_path);
+ QFileInfo parent_local = local_files[parent_name];
+ RemoteFileInfo parent_remote = remote_files[parent_name];
+ Q_ASSERT(local.exists());
+ if (remote.isDir() && local.isDir()) {
+ if (equalDateTime(remote.lastModified(), local.lastModified())) {
+ if (local.lastModified().toTime_t() == 0) {
+ // Workaround an issue if the local folder is a mountpoint
+ actions_update_dirs += ActionInfo(ACTION_PULL_METADATA, local);
+ }
+ // Nothing to do
+ } else if (remote.lastModified() > local.lastModified()) {
+ actions_update_dirs += ActionInfo(ACTION_PUSH_METADATA, local);
+ } else {
+ actions_update_dirs += ActionInfo(ACTION_PULL_METADATA, local);
+ }
+ } else if (remote.isDir() == local.isDir() &&
+ equalDateTime(remote.lastModified(), local.lastModified()) &&
+ remote.size() == local.size()) {
+ // Nothing to do
+ } else if (remote.isDir() != local.isDir()) {
+ // Dir to file transformation, delete the most recent file
+ if (parent_remote.lastModified() > parent_local.lastModified()) {
+ if (local.isDir()) {
+ actions_remove_dirs += ActionInfo(ACTION_PUSH_DELETE, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PUSH_DELETE, local);
+ }
+ } else {
+ if (remote.isDir()) {
+ actions_remove_dirs += ActionInfo(ACTION_PULL_DELETE, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PULL_DELETE, local);
+ }
+ }
+ } else if (remote.lastModified() > local.lastModified()) {
+ if (remote.isDir()) {
+ actions_create_dirs += ActionInfo(ACTION_PUSH, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PUSH, local);
+ }
+ } else {
+ if (remote.isDir()) {
+ actions_create_dirs += ActionInfo(ACTION_PULL, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PULL, local);
+ }
+ }
+ } else {
+ // File is in remote, but not in local
+ qDebug() << " file deleted";
+ QString parent_name = findExistingCommonAncestor(wire_path, local_files, remote_files);
+ QFileInfo local(wireToLocalPath(wire_path)); // Create invalid QFileInfo
+ QFileInfo parent_local = local_files[parent_name];
+ RemoteFileInfo parent_remote = remote_files[parent_name];
+ if (parent_remote.lastModified() > parent_local.lastModified()) {
+ if (remote.isDir()) {
+ actions_create_dirs += ActionInfo(ACTION_PUSH, local);
+ actions_update_dirs += ActionInfo(ACTION_PUSH_METADATA, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PUSH, local);
+ }
+ } else {
+ if (remote.isDir()) {
+ actions_remove_dirs += ActionInfo(ACTION_PULL_DELETE, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PULL_DELETE, local);
+ }
+ }
+ }
+ }
+
+ foreach (const QString& path, new_local_files) {
+ // File is in local, but not in remote
+ QString parent_name = findExistingCommonAncestor(path, local_files, remote_files);
+ QFileInfo local = local_files[path];
+ QFileInfo parent_local = local_files[parent_name];
+ RemoteFileInfo parent_remote = remote_files[parent_name];
+ if (parent_remote.lastModified() > parent_local.lastModified()) {
+ if (local.isDir()) {
+ actions_remove_dirs += ActionInfo(ACTION_PUSH_DELETE, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PUSH_DELETE, local);
+ }
+ } else {
+ if (local.isDir()) {
+ actions_create_dirs += ActionInfo(ACTION_PULL, local);
+ actions_update_dirs += ActionInfo(ACTION_PULL_METADATA, local);
+ } else {
+ actions_with_files += ActionInfo(ACTION_PULL, local);
+ }
+ }
+ }
+
+ qSort(actions_create_dirs.begin(), actions_create_dirs.end(),
+ static_cast<bool (*)(const ActionInfo&, const ActionInfo&)>(lessPathDepthThan));
+ qSort(actions_remove_dirs.begin(), actions_remove_dirs.end(),
+ static_cast<bool (*)(const ActionInfo&, const ActionInfo&)>(morePathDepthThan));
+ qSort(actions_update_dirs.begin(), actions_update_dirs.end(),
+ static_cast<bool (*)(const ActionInfo&, const ActionInfo&)>(morePathDepthThan));
+
+ ActionInfoList actions = actions_create_dirs +
+ actions_with_files + actions_remove_dirs +
+ actions_update_dirs;
+ sendMessage(MSG_FILE_ACTIONS_REPLY, encodeActionInfoList(actions));
+}
+
+void ServerAgent::handlePullFile(const QString &path)
+{
+ QFile file(wireToLocalPath(path));
+ if (file.open(QIODevice::ReadOnly)) {
+ QByteArray ba = file.readAll();
+ if (_flags & SYNC_COMPRESS) {
+ int old_size = ba.size();
+ ba = Compressor::compress(ba);
+ qDebug() << "Compress" << old_size << "->" << ba.size();
+ }
+ file.close();
+ sendMessage(MSG_PULL_FILE_REPLY, ba);
+ } else {
+ qWarning() << "Failed to open file" << file.fileName() << "for reading";
+ }
+}
+
+void ServerAgent::handlePushedFile(const QByteArray &ba)
+{
+ if (_flags & SYNC_READ_ONLY) return;
+ int len;
+ QString wire_path = decodeFileNameItem(ba, &len);
+ QByteArray file_data = Compressor::decompress(ba.mid(len));
+
+ QFile file(wireToLocalPath(wire_path));
+ if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ file.write(file_data);
+ file.close();
+ } else {
+ qWarning() << "Failed to open file" << file.fileName() << "for writing";
+ }
+}
+
+void ServerAgent::handlePushedMetadata(const RemoteFileInfoList& list)
+{
+ if (_flags & SYNC_READ_ONLY) return;
+ foreach (const RemoteFileInfo& remote, list) {
+ QString local_path = wireToLocalPath(remote.name());
+ setLocalFileDateTime(local_path, remote.lastModified());
+ }
+}
+
+void ServerAgent::handleDeleteFile(const QString &path)
+{
+ if (_flags & SYNC_READ_ONLY) return;
+ QString local_path = wireToLocalPath(path);
+ if (!QDir().remove(local_path)) {
+ qWarning() << "Failed to remove file" << local_path;
+ }
+}