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/agent.cc | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 distfoldd/agent.cc (limited to 'distfoldd/agent.cc') diff --git a/distfoldd/agent.cc b/distfoldd/agent.cc new file mode 100644 index 0000000..a718230 --- /dev/null +++ b/distfoldd/agent.cc @@ -0,0 +1,337 @@ +#include "agent.h" + +#ifdef Q_OS_UNIX +#include +#include +#endif + +Agent::Agent(QSslSocket *socket, const QDir& dir, SyncFlags flags, QObject *parent) : + QObject(parent), _dir(dir), _subPath("/"), _flags(flags), _socket(socket) +{ + connect(_socket, SIGNAL(readyRead()), SLOT(handleDataAvailable())); + connect(_socket, SIGNAL(sslErrors(QList)), SLOT(handleSslErrors(QList))); + connect(_socket, SIGNAL(disconnected()), SLOT(handleDisconnected())); +} + +Agent::RemoteFileInfo::RemoteFileInfo() : + _name(), _type(FILE_TYPE_NONE), _mtime(), _size(0) +{ +} + +Agent::RemoteFileInfo::RemoteFileInfo(const QString &name, FileType type, const QDateTime &mtime, qint64 size) : + _name(name), _type(type), _mtime(mtime), _size(size) +{ +} + +Agent::ActionInfo::ActionInfo(FileAction action, const QFileInfo &fileInfo) : + _action(action), _info(fileInfo) +{ +} + +Agent::RemoteActionInfo::RemoteActionInfo(FileAction action, const QString &name, FileType type, const QDateTime &mtime, qint64 size) : + _action(action), _info(name, type, mtime, size) +{ + +} + +void Agent::sendMessage(MessageType msg, const QByteArray &content) +{ + const int header_len = sizeof(MessageHeader); + QByteArray ba(header_len + content.size(), '\0'); + + MessageHeader *h = reinterpret_cast(ba.data()); + h->msg = msg; + h->len = content.size(); + if (h->len > 0) { + ba.replace(header_len, content.size(), content); + } + Q_ASSERT(ba.size() == header_len + content.size()); + + qDebug() << "Sending " << msg << content.size(); + + _socket->write(ba); +} + +int Agent::inBufRequiredData() const +{ + const int header_len = sizeof(MessageHeader); + if (_inBuf.size() < header_len) { + return header_len; + } else { + const MessageHeader *h; + h = reinterpret_cast(_inBuf.data()); + return header_len + h->len; + } +} + +bool Agent::equalDateTime(const QDateTime& dt1, const QDateTime& dt2) +{ + const qint64 threshold = 5 * 1000; + qint64 msecs = dt1.msecsTo(dt2); + return msecs > -threshold && msecs < threshold; +} + +void Agent::setLocalFileDateTime(const QString &path, const QDateTime &dt) +{ +#ifdef Q_OS_UNIX + const char *filename = path.toLocal8Bit().constData(); + struct utimbuf times; + times.actime = dt.toTime_t(); + times.modtime = dt.toTime_t(); + int rc = utime(filename, ×); + if (rc != 0) { + qWarning() << "Could not set local mtime of" << path; + } +#endif +} + +QString Agent::wireToLocalPath(const QString &path) +{ + return _dir.absolutePath() + _subPath + path; +} + +QString Agent::localToWirePath(const QString& path) +{ + const QString& basePath = _dir.absolutePath() + _subPath; + Q_ASSERT(_subPath.endsWith('/')); + QString wire_path(path); + if (path.startsWith(basePath)) { + wire_path.remove(0, basePath.size()); + } else if (path + '/' == basePath) { + wire_path = QString(); // This is the root dir. + } else { + qWarning() << "Where does this come from?" << path; + } + return wire_path; +} + +QString Agent::wireParentPath(const QString &path) +{ + int index = path.lastIndexOf('/'); + if (index == -1) return QString(""); + else return path.left(index); +} + +QString Agent::findExistingCommonAncestor(const QString& path, + const QHash& local_files, + const QHash& remote_files) +{ + QString ancestor = path; + do { + ancestor = wireParentPath(ancestor); + } while (!local_files.contains(ancestor) || !remote_files.contains(ancestor)); + return ancestor; +} + +bool Agent::lessPathDepthThan(const QString& s1, const QString& s2) +{ + int d1 = s1.count('/'), d2 = s2.count('/'); + return d1 < d2; +} + +bool Agent::morePathDepthThan(const QString& s1, const QString& s2) +{ + int d1 = s1.count('/'), d2 = s2.count('/'); + return d1 > d2; +} + +QFileInfoList Agent::scanFiles(const QDir& dir) +{ + QFileInfoList all; + QFileInfoList sub = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + + all << QFileInfo(dir, "."); + + foreach (const QFileInfo &info, sub) { + if (info.isDir()) { + all << scanFiles(QDir(info.absoluteFilePath())); + } else { + all << info; + } + } + + return all; +} + +QByteArray Agent::encodeFileInfoList(const QFileInfoList& list) +{ + QByteArray ba; + if (list.empty()) return ba; + + ba.reserve(list.size() * (sizeof(FileListItem) + 10)); + + foreach (const QFileInfo& info, list) { + QString path = localToWirePath(info.absoluteFilePath()); + FileListItem item; + item.size = info.size(); + + if (info.isDir() && _flags & SYNC_PULL) { + // If we are pulling, then simulate all of our local directories being old, + // so that we get all the new file creations + // Existing files should be compared with the proper mtimes though. + item.mtime = 0; + } else { + item.mtime = info.lastModified().toTime_t(); + } + + if (info.isDir()) { + item.type = FILE_TYPE_DIR; + } else if (info.isFile()) { + item.type = FILE_TYPE_FILE; + } else { + qWarning() << "What is this?" << path; + continue; + } + QByteArray utfPath = encodeFileName(path); + + item.name_len = utfPath.size(); + ba.append(reinterpret_cast(&item), sizeof(FileListItem)); + ba.append(utfPath.constData()); + } + + return ba; +} + +Agent::RemoteFileInfoList Agent::decodeFileInfoList(const QByteArray& ba) +{ + RemoteFileInfoList list; + + int pos = 0; + while (pos < ba.size()) { + const FileListItem *item; + item = reinterpret_cast(&(ba.data()[pos])); + + list.append(RemoteFileInfo(QString::fromUtf8(&item->name[0], + item->name_len), + static_cast(item->type), + QDateTime::fromTime_t(item->mtime), + item->size)); + + pos += sizeof(FileListItem) + item->name_len; + } + + return list; +} + +QByteArray Agent::encodeActionInfoList(const ActionInfoList &list) +{ + QByteArray ba; + + ba.reserve(list.size() * (sizeof(ActionItem) + 10)); + + foreach (const ActionInfo& info, list) { + const QFileInfo fileInfo = info.fileInfo(); + QString path = localToWirePath(fileInfo.absoluteFilePath()); + ActionItem item; + item.action = info.action(); + item.size = fileInfo.size(); + item.mtime = fileInfo.lastModified().toTime_t(); + + if (fileInfo.isDir()) { + item.type = FILE_TYPE_DIR; + } else if (fileInfo.isFile()) { + item.type = FILE_TYPE_FILE; + } else { + item.type = FILE_TYPE_NONE; // Actions might refer to no-longer existing files + } + + QByteArray utfPath = path.toUtf8(); + + item.name_len = utfPath.size(); + ba.append(reinterpret_cast(&item), sizeof(item)); + ba.append(utfPath.constData()); + } + + return ba; +} + +Agent::RemoteActionInfoList Agent::decodeActionInfoList(const QByteArray& ba) +{ + RemoteActionInfoList list; + + int pos = 0; + while (pos < ba.size()) { + const ActionItem *item; + item = reinterpret_cast(&(ba.data()[pos])); + + list.append(RemoteActionInfo(static_cast(item->action), + QString::fromUtf8(&item->name[0], + item->name_len), + static_cast(item->type), + QDateTime::fromTime_t(item->mtime), + item->size)); + + pos += sizeof(ActionItem) + item->name_len; + } + + return list; +} + +bool Agent::lessPathDepthThan(const ActionInfo& a1, const ActionInfo& a2) +{ + return lessPathDepthThan(a1.fileInfo().absoluteFilePath(), + a2.fileInfo().absoluteFilePath()); +} + +bool Agent::morePathDepthThan(const ActionInfo& a1, const ActionInfo& a2) +{ + return morePathDepthThan(a1.fileInfo().absoluteFilePath(), + a2.fileInfo().absoluteFilePath()); +} + +QByteArray Agent::encodeFileName(const QString& wire_path) +{ + return wire_path.toUtf8(); +} + +QString Agent::decodeFileName(const QByteArray& ba) +{ + return QString::fromUtf8(ba); +} + +QByteArray Agent::encodeFileNameItem(const QString& wire_path) +{ + QByteArray utf8 = encodeFileName(wire_path); + QByteArray ba(sizeof(FileNameItem), 0); + FileNameItem *item = reinterpret_cast(ba.data()); + item->name_len = utf8.length(); + ba.append(utf8); + return ba; +} + +QString Agent::decodeFileNameItem(const QByteArray& ba, int* length) +{ + const FileNameItem *item = reinterpret_cast(ba.data()); + *length = sizeof(FileNameItem) + item->name_len; + QString wire_path = decodeFileName(ba.mid(sizeof(FileNameItem), item->name_len)); + return wire_path; +} + +void Agent::handleDataAvailable() +{ + do { + _inBuf.append(_socket->readAll()); + + while (_inBuf.size() >= inBufRequiredData()) { + MessageHeader *h = reinterpret_cast(_inBuf.data()); + QByteArray data; + if (h->len > 0) { + data = _inBuf.mid(sizeof(MessageHeader), h->len); + } + handleMessage(static_cast(h->msg), data); + _inBuf.remove(0, inBufRequiredData()); + } + } while (_socket->bytesAvailable() > 0); +} + +void Agent::handleSslErrors(const QList &errors) +{ + qDebug() << "SSL errors:" << errors; + _socket->ignoreSslErrors(); // TODO For now +} + +void Agent::handleDisconnected() +{ + qDebug() << "Disconnected at" << QDateTime::currentDateTime(); + deleteLater(); +} -- cgit v1.2.3