#include #include "clientagent.h" ClientAgent::ClientAgent(const QHostAddress& addr, uint port, const QDir& local_dir, SyncFlags flags, QObject *parent) : Agent(new QSslSocket, local_dir, flags, parent) { qDebug() << "Starting client agent at" << QDateTime::currentDateTime(); _socket->setParent(this); // Can't set parent until QObject constructed _socket->connectToHostEncrypted(addr.toString(), port); sendMessage(MSG_HELLO); _state = STATE_HELLO; } void ClientAgent::handleMessage(MessageType msg, const QByteArray &data) { qDebug() << "Client::handleMessage" << msg << data.size(); switch (msg) { case MSG_HELLO_REPLY: Q_ASSERT(_state == STATE_HELLO); qDebug() << "Hello reply"; _state = STATE_FILE_LIST; sendFileList(); break; case MSG_FILE_ACTIONS_REPLY: Q_ASSERT(_state == STATE_FILE_LIST); handleActionInfoList(decodeActionInfoList(data)); break; case MSG_PULL_FILE_REPLY: Q_ASSERT(_state == STATE_FILE_ACTIONS && !_pendingActions.empty()); handlePulledFile(data); break; default: qWarning() << "Unknown message"; break; } } void ClientAgent::sendFileList() { QFileInfoList list = scanFiles(QDir(wireToLocalPath(_subPath))); sendMessage(MSG_FILE_LIST, encodeFileInfoList(list)); } void ClientAgent::handleActionInfoList(const RemoteActionInfoList &list) { foreach (const RemoteActionInfo& info, list) { switch (info.action()) { case ACTION_NONE: qDebug() << " = " << info.fileInfo().name(); break; case ACTION_PULL: qDebug() << " < " << info.fileInfo().name(); break; case ACTION_PULL_METADATA: qDebug() << " " << info.fileInfo().name(); break; case ACTION_PUSH_METADATA: qDebug() << " >m" << info.fileInfo().name(); break; case ACTION_PUSH_DELETE: qDebug() << " >d" << info.fileInfo().name(); break; } } _pendingActions = list; _state = STATE_FILE_ACTIONS; executeNextAction(); } void ClientAgent::executeNextAction() { if (_pendingActions.empty()) { qDebug() << "Done"; emit finished(); sendMessage(MSG_BYE); _socket->flush(); _socket->close(); return; } qDebug() << "Remaining actions" << _pendingActions.count(); const RemoteActionInfo& info = _pendingActions.first(); const QString wire_path = info.fileInfo().name(); const QString local_path = wireToLocalPath(wire_path); switch (info.action()) { case ACTION_PULL: if (_flags & SYNC_READ_ONLY) break; if (info.fileInfo().isDir()) { if (!QDir().mkpath(local_path)) { qWarning() << "Failed to create local path" << local_path; } } else { sendMessage(MSG_PULL_FILE, encodeFileName(wire_path)); return; // Wait for the pulled file message. } break; case ACTION_PULL_METADATA: if (_flags & SYNC_READ_ONLY) break; setLocalFileDateTime(local_path, info.fileInfo().lastModified()); break; case ACTION_PULL_DELETE: handleDeleteFile(wire_path); break; case ACTION_PUSH: handlePushFile(wire_path); break; case ACTION_PUSH_METADATA: sendMessage(MSG_PUSH_FILE_METADATA, encodeFileInfoList(QFileInfoList() << QFileInfo(local_path))); break; case ACTION_PUSH_DELETE: sendMessage(MSG_DELETE_FILE, encodeFileName(wire_path)); break; default: qWarning() << "Unknown action" << info.action(); break; } _pendingActions.removeFirst(); executeNextAction(); } void ClientAgent::handlePulledFile(const QByteArray &data) { const RemoteActionInfo& info = _pendingActions.takeFirst(); if (_flags & SYNC_READ_ONLY) return; QString local_path(wireToLocalPath(info.fileInfo().name())); QFile file(local_path); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { file.write(Compressor::decompress(data)); file.close(); setLocalFileDateTime(local_path, info.fileInfo().lastModified()); } else { qWarning() << "Failed to open" << file.fileName() << "for writing"; } executeNextAction(); } void ClientAgent::handlePushFile(const QString &path) { QFile file(wireToLocalPath(path)); if (file.open(QIODevice::ReadOnly)) { QByteArray file_data = file.readAll(); if (_flags & SYNC_COMPRESS) file_data = Compressor::compress(file_data); QByteArray ba = encodeFileNameItem(path) + file_data; sendMessage(MSG_PUSH_FILE, ba); } else { qWarning() << "Failed to open file" << file.fileName() << "for reading"; } } void ClientAgent::handleDeleteFile(const QString &wire_path) { if (_flags & SYNC_READ_ONLY) return; QFileInfo local(wireToLocalPath(wire_path)); QString local_path = local.absoluteFilePath(); if (local.isDir()) { if (!QDir().rmdir(local_path)) { qWarning() << "Failed to remove local dir" << local_path; } } else { if (!QDir().remove(local_path)) { qWarning() << "Failed to remove local file" << local_path; } } }