Files
pip/libs/main/io_utils/pifiletransfer.h
2026-03-07 17:00:45 +03:00

245 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*! \file pifiletransfer.h
* \ingroup IO-Utils
* \~\brief
* \~english Transfer helper for files and directory trees over \a PIBaseTransfer
* \~russian Вспомогательный класс передачи файлов и деревьев каталогов поверх \a PIBaseTransfer
*/
/*
PIP - Platform Independent Primitives
Class for send and receive files and directories via PIBaseTransfer
Andrey Bychkov work.a.b@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PIFILETRANSFER_H
#define PIFILETRANSFER_H
#include "pibasetransfer.h"
#include "pidir.h"
#define __PIFILETRANSFER_VERSION 2
//! \ingroup IO-Utils
//! \~\brief
//! \~english Two-phase file transfer that first exchanges descriptions and then file contents.
//! \~russian Двухфазная передача файлов, которая сначала обменивается описаниями, а затем содержимым файлов.
class PIP_EXPORT PIFileTransfer: public PIBaseTransfer {
PIOBJECT_SUBCLASS(PIFileTransfer, PIBaseTransfer);
public:
//! \~english Constructs file transfer rooted at the current directory for receiving.
//! \~russian Создает файловую передачу с текущим каталогом как корнем приема.
PIFileTransfer();
//! \~english Stops active work and closes the current file handle.
//! \~russian Останавливает активную работу и закрывает текущий файловый дескриптор.
~PIFileTransfer();
//! \~english Stage of the file-transfer protocol.
//! \~russian Этап протокола передачи файлов.
enum StepType {
pft_None /** \~english No active stage \~russian Активный этап отсутствует */,
pft_Description /** \~english Exchange file list and metadata \~russian Обмен списком файлов и метаданными */,
pft_Data /** \~english Exchange file data blocks \~russian Обмен блоками данных файлов */
};
//! \~english File-system entry description extended with destination relative path.
//! \~russian Описание элемента файловой системы, расширенное относительным путем назначения.
struct PIP_EXPORT PFTFileInfo: public PIFile::FileInfo {
//! \~english Constructs transferable file info from base \a PIFile::FileInfo.
//! \~russian Создает передаваемое описание файла из базового \a PIFile::FileInfo.
PFTFileInfo(const PIFile::FileInfo & fi = PIFile::FileInfo()): PIFile::FileInfo(fi) {}
//! \~english Relative destination path on the receiver side.
//! \~russian Относительный путь назначения на стороне приемника.
PIString dest_path;
};
#pragma pack(push, 1)
//! \~english Custom packet header used by the file-transfer protocol.
//! \~russian Пользовательский заголовок пакета, используемый протоколом передачи файлов.
struct PIP_EXPORT PFTHeader {
union {
struct {
//! \~english Three-byte protocol signature "PFT".
//! \~russian Трехбайтовая сигнатура протокола "PFT".
char sig[3]; // PFT
//! \~english Protocol version stored in the packet.
//! \~russian Версия протокола, сохраненная в пакете.
uchar version;
};
//! \~english Raw 32-bit representation of signature and version.
//! \~russian Сырое 32-битное представление сигнатуры и версии.
uint raw_sig;
};
//! \~english Current transfer step from \a StepType.
//! \~russian Текущий этап передачи из \a StepType.
int step; // PacketType
//! \~english File-transfer session identifier.
//! \~russian Идентификатор сессии передачи файлов.
int session_id;
//! \~english Returns whether header signature and protocol version are supported.
//! \~russian Возвращает, поддерживаются ли сигнатура заголовка и версия протокола.
bool check_sig() {
if (sig[0] != sign[0] || sig[1] != sign[1] || sig[2] != sign[2] || version != __PIFILETRANSFER_VERSION) return false;
return true;
}
};
#pragma pack(pop)
//! \~english Sends one file-system entry identified by "file".
//! \~russian Отправляет один элемент файловой системы, заданный "file".
bool send(const PIFile & file);
//! \~english Sends one file or directory by path.
//! \~russian Отправляет один файл или каталог по пути.
bool send(const PIString & file);
//! \~english Sends all files or directories listed in "files".
//! \~russian Отправляет все файлы или каталоги из списка "files".
bool send(const PIStringList & files);
//! \~english Sends one file-system entry described by "entry".
//! \~russian Отправляет один элемент файловой системы, описанный "entry".
bool send(PIFile::FileInfo entry) { return send(PIVector<PIFile::FileInfo>() << entry); }
//! \~english Sends file entries from "entries", recursively expanding directories.
//! \~russian Отправляет элементы файловой системы из "entries", рекурсивно раскрывая каталоги.
bool send(PIVector<PIFile::FileInfo> entries);
//! \~english Sets destination directory used for received files.
//! \~russian Устанавливает каталог назначения для принимаемых файлов.
void setDirectory(const PIDir & d) { dir = d; }
//! \~english Sets destination directory used for received files by path.
//! \~russian Устанавливает каталог назначения для принимаемых файлов по пути.
void setDirectory(const PIString & path) { dir.setDir(path); }
//! \~english Returns destination directory used for received files.
//! \~russian Возвращает каталог назначения для принимаемых файлов.
PIDir directory() const { return dir; }
//! \~english Returns whether the overall file transfer workflow is active.
//! \~russian Возвращает, активен ли общий процесс передачи файлов.
bool isStarted() const { return started_; }
//! \~english Returns current file path or scanning status string.
//! \~russian Возвращает путь текущего файла или строку состояния сканирования.
PIString curFile() const;
//! \~english Returns total size of the current file being processed.
//! \~russian Возвращает общий размер текущего обрабатываемого файла.
llong bytesFileAll() const { return bytes_file_all; }
//! \~english Returns processed size of the current file being processed.
//! \~russian Возвращает уже обработанный объем текущего файла.
llong bytesFileCur() const { return bytes_file_cur; }
//! \~english Returns pointer to the current file status string for bindings.
//! \~russian Возвращает указатель на строку состояния текущего файла для привязок.
const PIString * curFile_ptr() const { return &cur_file_string; }
//! \~english Returns pointer to \a bytesFileAll() for bindings.
//! \~russian Возвращает указатель на \a bytesFileAll() для привязок.
const llong * bytesFileAll_ptr() const { return &bytes_file_all; }
//! \~english Returns pointer to \a bytesFileCur() for bindings.
//! \~russian Возвращает указатель на \a bytesFileCur() для привязок.
const llong * bytesFileCur_ptr() const { return &bytes_file_cur; }
EVENT(receiveFilesStarted);
EVENT1(receiveFilesFinished, bool, ok);
EVENT(sendFilesStarted);
EVENT1(sendFilesFinished, bool, ok);
EVENT3(receiveFilesRequest, PIStringList, files, llong, total_bytes, bool *, ok);
//! \events
//! \{
//!
//! \fn void receiveFilesStarted()
//! \~english Emitted when a new incoming file-transfer workflow starts.
//! \~russian Генерируется при запуске нового входящего процесса передачи файлов.
//!
//! \fn void receiveFilesFinished(bool ok)
//! \~english Emitted when receiving files finishes with result "ok".
//! \~russian Генерируется, когда прием файлов завершается с результатом "ok".
//!
//! \fn void sendFilesStarted()
//! \~english Emitted when preparing and sending files starts.
//! \~russian Генерируется при начале подготовки и отправки файлов.
//!
//! \fn void sendFilesFinished(bool ok)
//! \~english Emitted when sending files finishes with result "ok".
//! \~russian Генерируется, когда отправка файлов завершается с результатом "ok".
//!
//! \fn void receiveFilesRequest(PIStringList files, llong total_bytes, bool * ok)
//! \~english
//! Emitted after the description phase so user code can accept or reject the incoming file set.
//! \~russian
//! Генерируется после фазы описания, чтобы пользовательский код мог принять или отклонить входящий набор файлов.
//!
//! \}
private:
static const char sign[];
PIVector<PFTFileInfo> files_;
PIString cur_file_string;
llong bytes_file_all, bytes_file_cur;
PFTHeader pftheader;
PIDir dir;
PIFile work_file;
PIByteArray desc;
PIDir scan_dir;
bool started_, scanning;
PIMutex step_mutex;
bool sendFiles(const PIVector<PFTFileInfo> & files);
void processFile(int id, ullong start, PIByteArray & data);
void receivePart(Part fi, PIByteArray ba, PIByteArray pheader) override;
PIByteArray buildPacket(Part fi) override;
PIByteArray customHeader() override;
void beginReceive() override;
EVENT_HANDLER1(void, send_finished, bool, ok);
EVENT_HANDLER1(void, receive_finished, bool, ok);
};
BINARY_STREAM_WRITE(PIFileTransfer::PFTHeader) {
s << v.raw_sig << v.step << v.session_id;
return s;
}
BINARY_STREAM_READ(PIFileTransfer::PFTHeader) {
s >> v.raw_sig >> v.step >> v.session_id;
return s;
}
BINARY_STREAM_WRITE(PIFileTransfer::PFTFileInfo) {
s << v.dest_path << v.size << v.time_access << v.time_modification << v.flags << v.id_user << v.id_group << v.perm_user.raw
<< v.perm_group.raw << v.perm_other.raw;
return s;
}
BINARY_STREAM_READ(PIFileTransfer::PFTFileInfo) {
s >> v.dest_path >> v.size >> v.time_access >> v.time_modification >> v.flags >> v.id_user >> v.id_group >> v.perm_user.raw >>
v.perm_group.raw >> v.perm_other.raw;
return s;
}
inline PICout operator<<(PICout s, const PIFileTransfer::PFTFileInfo & v) {
s.saveAndSetControls(0);
s << "FileInfo(\"" << v.dest_path << "\", " << PIString::readableSize(v.size) << ", " << v.perm_user.toString() << " "
<< v.perm_group.toString() << " " << v.perm_other.toString() << ", " << v.time_access.toString() << ", "
<< v.time_modification.toString() << ", 0x" << PICoutManipulators::Hex << v.flags << ")";
s.restoreControls();
return s;
}
#endif // PIFILETRANSFER_H