From c3a1946675855b299a2b36550cdf2c2f69d153aa Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Mon, 17 Sep 2012 23:03:03 +0200 Subject: initial import --- distfoldd/serveragent.cc | 225 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 distfoldd/serveragent.cc (limited to 'distfoldd/serveragent.cc') 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 + +#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 local_files; + QHash remote_files; + QSet 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(lessPathDepthThan)); + qSort(actions_remove_dirs.begin(), actions_remove_dirs.end(), + static_cast(morePathDepthThan)); + qSort(actions_update_dirs.begin(), actions_update_dirs.end(), + static_cast(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; + } +} -- cgit v1.2.3