/* 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 . */ #include "pifiletransfer.h" const char PIFileTransfer::sign[] = {'P', 'F', 'T'}; PIFileTransfer::PIFileTransfer() { for (uint i = 0; i < sizeof(sign); i++) pftheader.sig[i] = sign[i]; pftheader.version = __PIFILETRANSFER_VERSION; pftheader.session_id = 0; pftheader.step = pft_None; dir = PIDir::current(); started_ = scanning = false; bytes_file_all = bytes_file_cur = 0; CONNECT1(void, bool, this, sendFinished, this, send_finished); CONNECT1(void, bool, this, receiveFinished, this, receive_finished); } PIFileTransfer::~PIFileTransfer() { stop(); work_file.close(); } bool PIFileTransfer::send(const PIFile & file) { return send(file.path()); } bool PIFileTransfer::send(const PIString & file) { return send(PIVector() << PIFile::fileInfo(file)); } bool PIFileTransfer::send(const PIStringList & files) { PIVector fil; for (const auto & file: files) fil << PIFile::fileInfo(file); return send(fil); } bool PIFileTransfer::send(PIVector entries) { started_ = true; PIVector allEntries; for (int i = 0; i < entries.size_s(); i++) { allEntries << PFTFileInfo(entries[i]); allEntries.back().dest_path = entries[i].name(); if (entries[i].isDir()) { cur_file_string = "scanning ... "; scan_dir.setDir(entries[i].path); scanning = true; PIVector fls = scan_dir.allEntries(); scanning = false; scan_dir.up(); // piCout << "files rel to" << d.absolutePath(); for (uint j = 0; j < fls.size(); j++) { allEntries << PFTFileInfo(fls[j]); allEntries.back().dest_path = scan_dir.relative(fls[j].path); // piCout << " abs path" << fls[j].path; // piCout << " rel path" << allEntries.back().dest_path; } } } return sendFiles(allEntries); } bool PIFileTransfer::sendFiles(const PIVector & files) { files_ = files; PIStringList names; // piCoutObj << "prepare to send" << files_.size() << "files"; for (int i = 0; i < files_.size_s(); i++) { if (names.contains(files_[i].path)) { files_.remove(i); --i; } else names << files_[i].path; if (files_[i].isDir()) files_[i].size = 0; // piCout << "prepare" << i << files_[i].path << files_[i].dest_path << files_[i].name(); } randomize(); step_mutex.lock(); pftheader.session_id = randomi(); sendFilesStarted(); cur_file_string = "build session"; desc.clear(); desc << files_; pftheader.step = pft_Description; buildSession(PIVector() << Part(0, desc.size())); cur_file_string = ""; step_mutex.unlock(); if (!send_process()) { sendFilesFinished(false); started_ = false; return false; } step_mutex.lock(); pftheader.step = pft_Data; PIVector pts; for (int i = 0; i < files_.size_s(); i++) { pts << Part(i + 1, files_[i].size); } buildSession(pts); step_mutex.unlock(); bool ok = send_process(); sendFilesFinished(ok); started_ = false; return ok; } void PIFileTransfer::processFile(int id, ullong start, PIByteArray & data) { // piCout << "processFile" << id << files_.size(); PFTFileInfo fi = files_[id - 1]; bytes_file_all = fi.size; bytes_file_cur = start; cur_file_string = fi.dest_path; PIString path = dir.absolutePath() + dir.separator + fi.dest_path; // piCout << "receive" << path << fi.size << data.size(); if (fi.isDir()) { // piCoutObj << "make dir" << fi.entry.path; if (!PIDir::make(path)) { cur_file_string = "Error: While create directory \"" + path + "\""; piCoutObj << cur_file_string; stopReceive(); return; } } if (fi.isFile()) { if (work_file.path() != path || !work_file.isOpened()) { // piCout << "file close"; work_file.close(); // piCout << "new file" << path << work_file.path() << work_file.isOpened(); if (!work_file.open(path, PIIODevice::ReadWrite)) { cur_file_string = "Error: While open file \"" + path + "\""; piCoutObj << cur_file_string; stopReceive(); return; } PIFile::applyFileInfo(path, fi); if (work_file.size() > fi.size) { // piCoutObj << "error size" << work_file.size() << fi.size; work_file.clear(); work_file.resize(fi.size); // piCoutObj << "correct size" << work_file.size() << fi.size; } } // piCoutObj << "write file" << path << work_file.path() << work_file.size() << fi.entry.size << work_file.pos() << fi.fstart << // fi.fsize; if (work_file.size() < (llong)start) { // piCoutObj << "error pos size" << work_file.pos() << fi.fstart; work_file.resize(start); // piCoutObj << "correct pos size" << work_file.pos() << fi.fstart; } if (work_file.size() > fi.size) { piCoutObj << "****** error size" << work_file.size() << fi.size; work_file.clear(); work_file.resize(fi.size); piCoutObj << "****** correct size" << work_file.size() << fi.size; } // if (fi.fstart != work_file.pos()) piCoutObj << "error pos" << work_file.pos() << fi.fstart; work_file.seek(start); int rs = work_file.write(data.data(), data.size()); if (rs != data.size_s()) { cur_file_string = "Error: While write file \"" + fi.path + "\" (must " + PIString::fromNumber(data.size()) + ", but write " + PIString::fromNumber(rs) + ")"; piCoutObj << cur_file_string; stopReceive(); return; } } } PIByteArray PIFileTransfer::buildPacket(Part p) { // piCoutObj << "Step" << pftheader.step; // piCoutObj << "session id" << pftheader.session_id; PIByteArray ba; switch (pftheader.step) { case pft_None: stopSend(); return PIByteArray(); case pft_Description: ba.resize(p.size); memcpy(ba.data(), desc.data(p.start), p.size); return ba; case pft_Data: // piCout << "send data" << p.id << files_.size(); PIFile::FileInfo fi = files_[p.id - 1]; if (fi.isFile()) { // piCout << "send file" << fi.name() << fi.size; PIString path = fi.path; if (work_file.path() != path || !work_file.isOpened()) { if (!work_file.open(path, PIIODevice::ReadOnly)) { break_ = true; cur_file_string = "Error: While open file \"" + fi.path + "\""; piCoutObj << cur_file_string; stopSend(); return PIByteArray(); } } work_file.seek(p.start); ba.resize(p.size); int rs = work_file.read(ba.data(), ba.size()); if ((llong)rs != (llong)p.size) { break_ = true; cur_file_string = "Error: While read file \"" + fi.path + "\" (must " + PIString::fromNumber(p.size) + ", but read " + PIString::fromNumber(rs) + ")"; piCoutObj << cur_file_string; stopSend(); return PIByteArray(); } } cur_file_string = fi.path; bytes_file_all = fi.size; bytes_file_cur = p.start; return ba; } return ba; } void PIFileTransfer::receivePart(PIBaseTransfer::Part fi, PIByteArray ba, PIByteArray pheader) { PFTHeader h; // piCout << pheader.size() << sizeof(PFTHeader); pheader >> h; // piCout << h.session_id; PIMutexLocker ml(step_mutex); StepType st = (StepType)h.step; pftheader.step = st; if (!h.check_sig()) { cur_file_string = "Error: Check signature: File Transfer incompatable or invalid version!"; piCoutObj << cur_file_string; stopReceive(); return; } switch (st) { case pft_None: break; case pft_Description: pftheader.session_id = h.session_id; if (desc.size() < fi.start + fi.size) desc.resize(fi.start + fi.size); memcpy(desc.data(fi.start), ba.data(), ba.size_s()); break; case pft_Data: if (h.session_id == pftheader.session_id) processFile(fi.id, fi.start, ba); else stopReceive(); break; default: break; } } PIString PIFileTransfer::curFile() const { PIString s = cur_file_string; if (scanning) return s + scan_dir.scanDir(); return s; } PIByteArray PIFileTransfer::customHeader() { PIByteArray ba; ba << pftheader; return ba; } void PIFileTransfer::beginReceive() { if (pftheader.step == pft_None) { files_.clear(); // piCoutObj << "start receive" started_ = true; receiveFilesStarted(); } } void PIFileTransfer::receive_finished(bool ok) { step_mutex.lock(); StepType st = (StepType)pftheader.step; step_mutex.unlock(); if (st == pft_Description) { bool user_ok = true; if (ok) { // piCoutObj << desc.size() << PICoutManipulators::Hex << desc; desc >> files_; // piCoutObj << files_; PIStringList files; for (const auto & fi: files_) files << fi.dest_path; pause(); receiveFilesRequest(files, bytesAll(), &user_ok); } if (!ok || !user_ok) { pftheader.session_id--; piCoutObj << "receive aborted"; receiveFilesFinished(false); started_ = false; } else resume(); } if (st == pft_Data) { // piCoutObj << "file close"; work_file.close(); receiveFilesFinished(ok); started_ = false; } } void PIFileTransfer::send_finished(bool ok) { // piCoutObj << "file close"; if (pftheader.step == pft_Data) { work_file.close(); } }