Skip to content

Commit db79d30

Browse files
Updated the new feature of preventing overwrite to optimize for large folder transfers, how it works is that it makes a list of files with thier hash and then sends it before hand, so that the client can check multiple files then start sending, currently does not work with encryption.
1 parent 0a824b7 commit db79d30

2 files changed

Lines changed: 163 additions & 91 deletions

File tree

mainwindow.cpp

Lines changed: 162 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -591,10 +591,11 @@ MainWindow::MainWindow(QWidget *parent)
591591
{
592592
if (fromId != pairPartnerId)
593593
{
594-
QMessageBox::information(this, "Warning", "An Unrecognized client tried to send you a file!");
594+
QMessageBox::information(this, "Warning", "An Unrecognized client tried to send you a folder!");
595595
return;
596596
}
597597

598+
// 1. Parse directories
598599
QJsonArray directories = obj["directories"].toArray();
599600
QStringList folders;
600601
folders.reserve(directories.size());
@@ -609,10 +610,10 @@ MainWindow::MainWindow(QWidget *parent)
609610
recCipher->process(directoryNameBytes);
610611
directoryName = QString::fromUtf8(directoryNameBytes);
611612
}
612-
613613
folders.append(directoryName);
614614
}
615615

616+
// 2. Initialize UI (This prevents the crash!)
616617
QListWidgetItem *item = new QListWidgetItem(ui->MessageList);
617618
currentFileMessage = new fileMessage;
618619
item->setSizeHint(currentFileMessage->sizeHint());
@@ -623,6 +624,7 @@ MainWindow::MainWindow(QWidget *parent)
623624
filesToRecieve = obj["file_count"].toInt();
624625
sendingFolder = true;
625626

627+
// 3. Create the physical folders on the hard drive
626628
QString rootPathName = obj["root_path"].toString();
627629

628630
if (ui->ActivateEncryption->isChecked())
@@ -636,16 +638,51 @@ MainWindow::MainWindow(QWidget *parent)
636638
QString rootPath = transfer_file_path + "/" + rootPathName;
637639
root_folder_name = rootPathName;
638640

639-
// Use mkpath so nested dirs always create
640641
QDir().mkpath(rootPath);
641-
for (const QString &folder : folders)
642+
for (const QString &folder : folders) {
642643
QDir().mkpath(rootPath + folder);
644+
}
645+
646+
// 4. --- BATCH MANIFEST LOGIC ---
647+
QJsonArray manifest = obj["manifest"].toArray();
648+
QJsonArray skipList;
649+
650+
for (int i = 0; i < manifest.size(); ++i)
651+
{
652+
QJsonObject fileObj = manifest[i].toObject();
653+
QString relPath = fileObj["path"].toString();
654+
QString remoteHash = fileObj["hash"].toString();
655+
656+
QString fullLocalPath = rootPath + relPath;
657+
QFileInfo checkFile(fullLocalPath);
658+
659+
if (checkFile.exists())
660+
{
661+
QString localHash = fileChecksum(fullLocalPath, QCryptographicHash::Sha256);
662+
if (localHash == remoteHash)
663+
{
664+
skipList.append(relPath); // It matches! Tell sender to skip it.
665+
filesToRecieve--; // Lower the expected file count
666+
}
667+
}
668+
}
669+
670+
// 5. Send the Batch Reply back to the sender
671+
QJsonObject replyObj;
672+
replyObj["type"] = "batch_file_check_reply";
673+
replyObj["skip_list"] = skipList;
674+
replyObj["to"] = pairPartnerId;
643675

676+
QJsonDocument replyDoc(replyObj);
677+
socket->write(replyDoc.toJson(QJsonDocument::Compact) + '\n');
678+
679+
// If we skipped literally everything, finish up
644680
if (filesToRecieve <= 0)
645681
{
646682
currentFileMessage->setFileName("");
647-
currentFileMessage->setStatus("Directories Finished Transfering!");
683+
currentFileMessage->setStatus("Directories Finished Transferring!");
648684
currentFileMessage->setProgress(100);
685+
sendingFolder = false;
649686
}
650687

651688
continue;
@@ -833,25 +870,93 @@ MainWindow::MainWindow(QWidget *parent)
833870
}
834871
else if (type == "file_check_reply")
835872
{
836-
skip = obj["skip"].toBool();
837-
emit fileCheckReplyReceived();
873+
bool skip = obj["skip"].toBool();
838874

875+
if (skip)
876+
{
877+
QJsonObject finObj;
878+
finObj["type"] = "file_transfer_finished";
879+
finObj["to"] = pairPartnerId;
880+
QJsonDocument finDoc(finObj);
881+
socket->write(finDoc.toJson(QJsonDocument::Compact) + '\n');
839882

840-
shouldSendFile = true;
883+
if (!fileQueue.isEmpty())
884+
{
885+
// FIX: Pass true so the NEXT file also gets a hash check!
886+
sendFile(true);
887+
}
888+
else
889+
{
890+
if (currentFileMessage) {
891+
// (Using your cleanup logic here)
892+
currentFileMessage->setFileName(current_file_name);
893+
currentFileMessage->setStatus("Folder Finished Transferring!");
894+
currentFileMessage->setProgress(100);
895+
}
841896

842-
continue;
897+
sendingFolder = false;
898+
selectedFilePath = "";
899+
selectedFolderPath = "";
900+
901+
ui->sendButton->setEnabled(true);
902+
ui->pairButton->setEnabled(true);
903+
ui->MessageInput->setEnabled(true);
904+
}
905+
906+
continue;
907+
}
908+
else
909+
{
910+
sendFile(false);
911+
continue;
912+
}
843913
}
914+
else if (type == "batch_file_check_reply")
915+
{
916+
QJsonArray skipList = obj["skip_list"].toArray();
844917

845-
// Unknown / unhandled type: just continue draining
918+
// Convert the JSON array to a quick lookup list
919+
QStringList filesToSkip;
920+
for (int i = 0; i < skipList.size(); ++i) {
921+
filesToSkip.append(skipList[i].toString());
922+
}
923+
924+
// Filter our fileQueue
925+
QQueue<QString> filteredQueue;
926+
while (!fileQueue.isEmpty())
927+
{
928+
QString filePath = fileQueue.dequeue();
929+
QString relPath = QString(filePath).replace(selectedFolderPath, "");
930+
931+
if (!filesToSkip.contains(relPath)) {
932+
filteredQueue.enqueue(filePath); // We need to send this one
933+
}
934+
}
935+
936+
fileQueue = filteredQueue; // Replace the queue with the filtered one
937+
938+
// Now that we've stripped out all the matches, start sending!
939+
if (!fileQueue.isEmpty()) {
940+
sendFile(true); // Pass false because we already did the checks!
941+
} else {
942+
// Everything was skipped! Clean up UI.
943+
if (currentFileMessage) {
944+
currentFileMessage->setStatus("Folder Finished Transferring!");
945+
currentFileMessage->setProgress(100);
946+
}
947+
sendingFolder = false;
948+
selectedFolderPath = "";
949+
ui->sendButton->setEnabled(true);
950+
ui->pairButton->setEnabled(true);
951+
ui->MessageInput->setEnabled(true);
952+
}
953+
continue;
954+
}
846955
continue;
847956
}
848957

849-
// =========================
850-
// MODE 2: raw file bytes
851-
// =========================
852958
if (mode == 2)
853959
{
854-
// Consume as many raw bytes as are currently buffered, up to the file size.
855960
while (socket->bytesAvailable() > 0 && current_file_size < current_total_file_size)
856961
{
857962
qint64 remaining = current_total_file_size - current_file_size;
@@ -1168,19 +1273,24 @@ void MainWindow::scrollToBottom()
11681273
ui->MessageList->scrollToBottom();
11691274
}
11701275

1171-
void MainWindow::sendFile()
1276+
void MainWindow::sendFile(bool runCheck = true)
11721277
{
1173-
if (sendingFolder && fileQueue.isEmpty())
1278+
if (runCheck)
11741279
{
1175-
return;
1280+
if (sendingFolder && fileQueue.isEmpty()) return;
1281+
if (!fileQueue.isEmpty()) selectedFilePath = fileQueue.dequeue();
11761282
}
11771283

1178-
if (!fileQueue.isEmpty()) selectedFilePath = fileQueue.dequeue();
1284+
// FAILSAFE: If the path is empty, abort so we don't crash the sockets!
1285+
if (selectedFilePath.isEmpty()) return;
11791286

11801287
QFileInfo info(selectedFilePath);
1181-
QString fileName = sendingFolder? QString(selectedFilePath).replace(selectedFolderPath, "") : info.fileName();
1288+
QString fileName = sendingFolder ? QString(selectedFilePath).replace(selectedFolderPath, "") : info.fileName();
1289+
current_file_name = fileName;
11821290

1183-
if (!ui->OverwriteCheck->isChecked())
1291+
// 2. THE FIX: Only run the individual file check if Overwrite is OFF *AND* we aren't sending a folder.
1292+
// (Folders are pre-approved by the batch manifest, so we skip this!)
1293+
if (!ui->OverwriteCheck->isChecked() && runCheck && !sendingFolder)
11841294
{
11851295
QJsonObject jsonMessage;
11861296
jsonMessage["type"] = "file_check";
@@ -1193,51 +1303,7 @@ void MainWindow::sendFile()
11931303
socket->write(jsonString.toUtf8());
11941304
socket->flush();
11951305

1196-
QEventLoop loop;
1197-
1198-
// When your network handler emits fileCheckReplyReceived, the loop will exit
1199-
connect(this, &MainWindow::fileCheckReplyReceived, &loop, &QEventLoop::quit);
1200-
1201-
// This will block the execution of THIS function, but keep the UI and network alive
1202-
loop.exec();
1203-
1204-
if (skip)
1205-
{
1206-
skip = false;
1207-
1208-
// 1. Tell the receiver we "finished" this file so it decrements its filesToRecieve counter!
1209-
QJsonObject finObj;
1210-
finObj["type"] = "file_transfer_finished";
1211-
finObj["to"] = pairPartnerId;
1212-
QJsonDocument finDoc(finObj);
1213-
socket->write(finDoc.toJson(QJsonDocument::Compact) + '\n');
1214-
1215-
// 2. Keep the chain going! Send the next file, or finish up.
1216-
if (!fileQueue.isEmpty())
1217-
{
1218-
// Call sendFile again to process the next item in the queue
1219-
sendFile();
1220-
}
1221-
else
1222-
{
1223-
// If that was the last file in the folder, clean up your UI state
1224-
if (currentFileMessage) {
1225-
currentFileMessage->setFileName(fileName);
1226-
currentFileMessage->setStatus("Folder Finished Transferring!");
1227-
currentFileMessage->setProgress(100);
1228-
}
1229-
1230-
sendingFolder = false;
1231-
selectedFilePath = "";
1232-
selectedFolderPath = "";
1233-
1234-
ui->sendButton->setEnabled(true);
1235-
ui->pairButton->setEnabled(true);
1236-
ui->MessageInput->setEnabled(true);
1237-
}
1238-
1239-
return; // Now it's safe to return, we handled the next steps
1240-
}
1306+
return; // We return and let the socket listener take over
12411307
}
12421308

12431309
QJsonObject jsonMessage;
@@ -1296,6 +1362,9 @@ void MainWindow::sendFile()
12961362
ui->pairButton->setEnabled(false);
12971363
ui->MessageInput->setEnabled(false);
12981364

1365+
selectedFilePath = "";
1366+
ui->FilePathDisplay->setText(selectedFilePath);
1367+
12991368
if (!ui->HardSendCheck->isChecked())
13001369
{
13011370
uploadConn = connect(socket, &QTcpSocket::bytesWritten,
@@ -1475,30 +1544,46 @@ void MainWindow::sendDirectories()
14751544
fileQueue.clear();
14761545
QDirIterator itfolders(selectedFolderPath, QDir::AllEntries | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
14771546
QJsonArray folderPaths;
1478-
14791547
QVector<QString> filesToSend;
14801548

1549+
// --- BATCHING SETUP ---
1550+
QJsonArray manifestArray;
1551+
14811552
while (itfolders.hasNext())
14821553
{
14831554
auto entity = itfolders.nextFileInfo();
14841555

14851556
if (entity.isDir())
14861557
{
14871558
QString dirName = entity.absoluteFilePath().replace(selectedFolderPath, "");
1488-
14891559
if (ui->ActivateEncryption->isChecked())
14901560
{
14911561
if (sendCipher == nullptr) return;
14921562
QByteArray dirNameBytes = dirName.toUtf8();
14931563
sendCipher->process(dirNameBytes);
14941564
dirName = dirNameBytes.toBase64();
14951565
}
1496-
14971566
folderPaths.append(dirName);
14981567
}
1499-
else filesToSend.append(entity.filePath());
1568+
else
1569+
{
1570+
QString filePath = entity.filePath();
1571+
filesToSend.append(filePath);
1572+
1573+
// Calculate the hash (or metadata) right now
1574+
QString relativePath = QString(filePath).replace(selectedFolderPath, "");
1575+
1576+
QJsonObject fileInfo;
1577+
fileInfo["path"] = relativePath;
1578+
1579+
// NOTE: You can use fileChecksum here, or info.size() + info.lastModified() for even more speed!
1580+
fileInfo["hash"] = fileChecksum(filePath, QCryptographicHash::Sha256);
1581+
manifestArray.append(fileInfo);
1582+
}
15001583
}
15011584

1585+
// ... (Your existing rootPathName logic) ...
1586+
15021587
QString rootPathName = selectedFolderPath.split("/").last();
15031588

15041589
if (ui->ActivateEncryption->isChecked())
@@ -1516,38 +1601,29 @@ void MainWindow::sendDirectories()
15161601
jsonMessage["file_count"] = filesToSend.count();
15171602
jsonMessage["to"] = pairPartnerId;
15181603

1604+
// Attach the manifest to your directory builder message!
1605+
jsonMessage["manifest"] = manifestArray;
1606+
15191607
ui->statusbar->showMessage(QString::number(filesToSend.count()), 10000);
15201608

15211609
QJsonDocument doc(jsonMessage);
1522-
QString jsonString = doc.toJson(QJsonDocument::Compact) + '\n';
1523-
socket->write(jsonString.toUtf8());
1610+
socket->write(doc.toJson(QJsonDocument::Compact) + '\n');
15241611

15251612
sendingFolder = true;
15261613

1614+
// Set up UI
15271615
QListWidgetItem *item = new QListWidgetItem(ui->MessageList);
15281616
currentFileMessage = new fileMessage;
15291617
item->setSizeHint(currentFileMessage->sizeHint());
15301618
ui->MessageList->addItem(item);
15311619
ui->MessageList->setItemWidget(item, currentFileMessage);
15321620

1533-
foreach (QString f, filesToSend)
1534-
{
1621+
foreach (QString f, filesToSend) {
15351622
fileQueue.enqueue(f);
15361623
}
15371624

1538-
if (ui->HardSendCheck->isChecked())
1539-
{
1540-
while (!fileQueue.isEmpty()) {
1541-
sendFile();
1542-
}
1543-
} else sendFile();
1544-
1545-
if (filesToSend.count() <= 0)
1546-
{
1547-
currentFileMessage->setFileName("");
1548-
currentFileMessage->setStatus("Directories Finished Transfering!");
1549-
currentFileMessage->setProgress(100);
1550-
}
1625+
// IMPORTANT: DO NOT call sendFile() here anymore!
1626+
// We will wait for the receiver to send back the "batch_reply"
15511627
}
15521628

15531629
void MainWindow::on_sendButton_clicked()
@@ -1566,7 +1642,6 @@ void MainWindow::on_sendButton_clicked()
15661642
else if (!selectedFolderPath.isEmpty()) sendDirectories();
15671643

15681644
ui->MessageInput->setText("");
1569-
selectedFilePath = "";
15701645
ui->FilePathDisplay->setText(selectedFilePath);
15711646
ui->RemoveFileButton->setVisible(false);
15721647
}

0 commit comments

Comments
 (0)