/*! \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 . */ #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() << entry); } //! \~english Sends file entries from "entries", recursively expanding directories. //! \~russian Отправляет элементы файловой системы из "entries", рекурсивно раскрывая каталоги. bool send(PIVector 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 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 & 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