@@ -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
15531629void 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