#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; } }