void TransferSender::writeItemData() { // Read the next chunk of data from the file - no more than either the size // of the buffer or the amount of data remaining in the file QByteArray buffer; buffer.resize(mBufferSize); qint64 bytesRead = mFile.read(buffer.data(), qMin(mFileBytesRemaining, static_cast<qint64>(mBufferSize))); // Ensure that a valid number of bytes were read if(bytesRead < 0 && mFileBytesRemaining) { writeErrorPacket(tr("Unable to read from %1").arg(mFile.fileName())); return; } // Write a packet containing the data that was just read writeBinaryPacket(buffer.left(bytesRead)); // Update the number of bytes remaining in the file and the total transferred mFileBytesRemaining -= bytesRead; mTransferBytes += bytesRead; // Provide a progress update updateProgress(); // If there are no bytes remaining in the file close it and move on if(!mFileBytesRemaining) { mFile.close(); nextItem(); } }
void Transfer::processPacket() { // Grab the data from the front of the buffer const char type = mBuffer.at(0); QByteArray data = mBuffer.mid(1, mBufferSize - 1); mBuffer.remove(0, mBufferSize); mBufferSize = 0; // Process the data based on the type switch (static_cast<PacketType>(type)) { case PacketType::Success: { finish(TransferModel::Succeeded); break; } case PacketType::Error: { // An error occurred - grab the error message mError = QString::fromUtf8(data); emit dataChanged({TransferModel::ErrorRole}); finish(TransferModel::Failed); break; } case PacketType::Json: { // Verify the JSON is valid and pass it along QJsonDocument document = QJsonDocument::fromJson(data); if (document.isObject()) { processJsonPacket(document.object()); } else { writeErrorPacket(tr("Unable to read JSON packet")); } break; } case PacketType::Binary: { // Pass the packet along unmodified processBinaryPacket(data); break; } default: // Any other type of packet is an error writeErrorPacket(tr("Unrecognized packet received")); } }
void Transfer::cancel() { // Ensure that the transfer is actually in progress before trying to cancel it if (mState == TransferModel::Failed || mState == TransferModel::Succeeded) { qWarning("Cannot cancel a transfer that has completed"); return; } // Canceling a request is considered an "error" and is treated the same way // Only attempt to report the error if the transfer isn't already finished if (mProtocolState != ProtocolState::Finished) { writeErrorPacket(tr("Transfer was canceled")); } }
void Transfer::onReadyRead() { // Add all of the new data to the buffer mBuffer.append(mSocket->readAll()); // Process as many packets as can be read from the buffer forever { // If the transfer is finished, ignore any packets being received if (mProtocolState == ProtocolState::Finished) { break; } // If the size of the packet is not yet known attempt to read it if (!mBufferSize) { // See if there is enough data in the buffer to read the size if (static_cast<size_t>(mBuffer.size()) >= sizeof(mBufferSize)) { // memcpy must be used in order to avoid alignment issues // Also, the integer uses little endian byte order // so convert it to the host format if necessary memcpy(&mBufferSize, mBuffer.constData(), sizeof(mBufferSize)); mBufferSize = qFromLittleEndian(mBufferSize); mBuffer.remove(0, sizeof(mBufferSize)); // A packet size of zero is an error if (!mBufferSize) { writeErrorPacket(tr("Empty packet received")); break; } } else { break; } } // If the buffer contains enough data to read the packet, then do so if (mBuffer.size() >= mBufferSize) { processPacket(); } else { break; } } }
void TransferSender::writeItemHeader() { // Build the header describing the item QJsonObject header = QJsonObject::fromVariantMap({ { "name", mCurrentItem->relativeFilename() }, { "directory", mCurrentItem->isDir() }, { "created", QString::number(mCurrentItem->created()) }, { "last_modified", QString::number(mCurrentItem->lastModified()) }, { "last_read", QString::number(mCurrentItem->lastRead()) } }); if(mCurrentItem->isDir()) { // If the item is a directory, write the // header and move to the next item writeJsonPacket(header); nextItem(); } else { // Set the filename and attempt to open the file mFile.setFileName(mCurrentItem->absoluteFilename()); if(!mFile.open(QIODevice::ReadOnly)) { writeErrorPacket(tr("Unable to open %1").arg(mCurrentItem->absoluteFilename())); return; } // Obtain the file size, add it to the header, and send it mFileBytesRemaining = mFile.size(); header.insert("size", QString::number(mFileBytesRemaining)); writeJsonPacket(header); // If the file is empty, there's no need to send its contents if(!mFileBytesRemaining) { mFile.close(); nextItem(); } } }
void TransferSender::processBinaryPacket(const QByteArray &) { // Binary packets should never be received by the sender writeErrorPacket(tr("Unexpected binary packet received")); }
void TransferSender::processJsonPacket(const QJsonObject &) { // JSON packets should never be received by the sender writeErrorPacket(tr("Unexpected JSON packet received")); }