🔧 Improve file transfer system integration and message handling
- File Transfer Integration: Refactored file transfer system initialization with better error handling and automatic retry mechanisms - Message Filtering: Added comprehensive message filtering system to prevent file transfer and system messages from appearing in chat UI - Callback System: Simplified file transfer callback system - removed progress notifications from chat to reduce noise - System Message Deduplication: Implemented notification flags to prevent duplicate system messages (verification, security upgrades, etc.) - Error Handling: Enhanced error handling with graceful fallbacks instead of throwing exceptions that could break connections - UI Message Delivery: Added `deliverMessageToUI()` method with built-in filtering for system/file messages - DataChannel Event Handling: Improved onmessage handler with early filtering for file transfer messages - Global Integration: Better integration with global file transfer system (`window.fileTransferSystem`) - Connection Stability: Removed aggressive reconnection attempts that could cause session closure - Debug Logging: Enhanced debug logging for better troubleshooting - File transfer messages now bypass chat UI entirely - System messages are deduplicated using internal flags - Better separation of concerns between WebRTC and file transfer systems - More robust initialization sequence with proper cleanup - Improved message routing and processing pipeline - Fixed duplicate system notifications - Resolved file transfer message leakage into chat - Improved connection state management - Better handling of initialization race conditions
This commit is contained in:
28
index.html
28
index.html
@@ -2963,6 +2963,34 @@
|
||||
}
|
||||
|
||||
const handleMessage = (message, type) => {
|
||||
// Дополнительная проверка на файловые и системные сообщения
|
||||
if (typeof message === 'string' && message.trim().startsWith('{')) {
|
||||
try {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
const blockedTypes = [
|
||||
'file_transfer_start',
|
||||
'file_transfer_response',
|
||||
'file_chunk',
|
||||
'chunk_confirmation',
|
||||
'file_transfer_complete',
|
||||
'file_transfer_error',
|
||||
'heartbeat',
|
||||
'verification',
|
||||
'verification_response',
|
||||
'peer_disconnect',
|
||||
'key_rotation_signal',
|
||||
'key_rotation_ready',
|
||||
'security_upgrade'
|
||||
];
|
||||
if (parsedMessage.type && blockedTypes.includes(parsedMessage.type)) {
|
||||
console.log(`🛑 Blocked system/file message from chat: ${parsedMessage.type}`);
|
||||
return; // Не показываем системные и файловые сообщения в чате
|
||||
}
|
||||
} catch (parseError) {
|
||||
// Не JSON - это нормально для обычных текстовых сообщений
|
||||
}
|
||||
}
|
||||
|
||||
addMessageWithAutoScroll(message, type);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// File Transfer Component for Chat Interface
|
||||
// File Transfer Component for Chat Interface - Fixed Version
|
||||
const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
const [dragOver, setDragOver] = React.useState(false);
|
||||
const [transfers, setTransfers] = React.useState({ sending: [], receiving: [] });
|
||||
@@ -17,19 +17,26 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
return () => clearInterval(interval);
|
||||
}, [isConnected, webrtcManager]);
|
||||
|
||||
// Setup file transfer callbacks
|
||||
// Setup file transfer callbacks - ИСПРАВЛЕНИЕ: НЕ отправляем промежуточные сообщения в чат
|
||||
React.useEffect(() => {
|
||||
if (!webrtcManager) return;
|
||||
|
||||
webrtcManager.setFileTransferCallbacks(
|
||||
// Progress callback
|
||||
// Progress callback - ТОЛЬКО обновляем UI, НЕ отправляем в чат
|
||||
(progress) => {
|
||||
console.log(`📁 UI Progress: ${progress.fileName}: ${progress.progress.toFixed(1)}% (${progress.status})`);
|
||||
|
||||
// Обновляем только локальное состояние
|
||||
const currentTransfers = webrtcManager.getFileTransfers();
|
||||
setTransfers(currentTransfers);
|
||||
|
||||
// НЕ отправляем сообщения в чат!
|
||||
},
|
||||
|
||||
// File received callback
|
||||
// File received callback - показываем только финальное уведомление
|
||||
(fileData) => {
|
||||
console.log(`📥 File received in UI: ${fileData.fileName}`);
|
||||
|
||||
// Auto-download received file
|
||||
const url = URL.createObjectURL(fileData.fileBlob);
|
||||
const a = document.createElement('a');
|
||||
@@ -41,13 +48,19 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
// Update transfer list
|
||||
const currentTransfers = webrtcManager.getFileTransfers();
|
||||
setTransfers(currentTransfers);
|
||||
|
||||
// ИСПРАВЛЕНИЕ: НЕ дублируем системные сообщения
|
||||
// Финальное уведомление уже отправляется в WebRTC менеджере
|
||||
},
|
||||
|
||||
// Error callback
|
||||
(error) => {
|
||||
console.error('File transfer error:', error);
|
||||
console.error('File transfer error in UI:', error);
|
||||
const currentTransfers = webrtcManager.getFileTransfers();
|
||||
setTransfers(currentTransfers);
|
||||
|
||||
// ИСПРАВЛЕНИЕ: НЕ дублируем сообщения об ошибках
|
||||
// Уведомления об ошибках уже отправляются в WebRTC менеджере
|
||||
}
|
||||
);
|
||||
}, [webrtcManager]);
|
||||
@@ -66,6 +79,7 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
console.log(`🚀 Starting file upload from UI: ${file.name}`);
|
||||
await webrtcManager.sendFile(file);
|
||||
} catch (error) {
|
||||
// Более мягкая обработка ошибок - не закрываем сессию
|
||||
@@ -117,6 +131,44 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
const getStatusIcon = (status) => {
|
||||
switch (status) {
|
||||
case 'metadata_sent':
|
||||
case 'preparing':
|
||||
return 'fas fa-cog fa-spin';
|
||||
case 'transmitting':
|
||||
case 'receiving':
|
||||
return 'fas fa-exchange-alt fa-pulse';
|
||||
case 'assembling':
|
||||
return 'fas fa-puzzle-piece fa-pulse';
|
||||
case 'completed':
|
||||
return 'fas fa-check text-green-400';
|
||||
case 'failed':
|
||||
return 'fas fa-times text-red-400';
|
||||
default:
|
||||
return 'fas fa-circle';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status) => {
|
||||
switch (status) {
|
||||
case 'metadata_sent':
|
||||
return 'Подготовка...';
|
||||
case 'transmitting':
|
||||
return 'Отправка...';
|
||||
case 'receiving':
|
||||
return 'Получение...';
|
||||
case 'assembling':
|
||||
return 'Сборка файла...';
|
||||
case 'completed':
|
||||
return 'Завершено';
|
||||
case 'failed':
|
||||
return 'Ошибка';
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
if (!isConnected) {
|
||||
return React.createElement('div', {
|
||||
className: "p-4 text-center text-muted"
|
||||
@@ -241,10 +293,24 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
className: "progress-fill bg-blue-400",
|
||||
style: { width: `${transfer.progress}%` }
|
||||
}),
|
||||
React.createElement('span', {
|
||||
React.createElement('div', {
|
||||
key: 'text',
|
||||
className: "progress-text text-xs"
|
||||
}, `${transfer.progress.toFixed(1)}% • ${transfer.status}`)
|
||||
className: "progress-text text-xs flex items-center justify-between"
|
||||
}, [
|
||||
React.createElement('span', {
|
||||
key: 'status',
|
||||
className: "flex items-center"
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: `${getStatusIcon(transfer.status)} mr-1`
|
||||
}),
|
||||
getStatusText(transfer.status)
|
||||
]),
|
||||
React.createElement('span', {
|
||||
key: 'percent'
|
||||
}, `${transfer.progress.toFixed(1)}%`)
|
||||
])
|
||||
])
|
||||
])
|
||||
),
|
||||
@@ -295,10 +361,24 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
className: "progress-fill bg-green-400",
|
||||
style: { width: `${transfer.progress}%` }
|
||||
}),
|
||||
React.createElement('span', {
|
||||
React.createElement('div', {
|
||||
key: 'text',
|
||||
className: "progress-text text-xs"
|
||||
}, `${transfer.progress.toFixed(1)}% • ${transfer.status}`)
|
||||
className: "progress-text text-xs flex items-center justify-between"
|
||||
}, [
|
||||
React.createElement('span', {
|
||||
key: 'status',
|
||||
className: "flex items-center"
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: `${getStatusIcon(transfer.status)} mr-1`
|
||||
}),
|
||||
getStatusText(transfer.status)
|
||||
]),
|
||||
React.createElement('span', {
|
||||
key: 'percent'
|
||||
}, `${transfer.progress.toFixed(1)}%`)
|
||||
])
|
||||
])
|
||||
])
|
||||
)
|
||||
|
||||
@@ -49,6 +49,22 @@ class EnhancedSecureWebRTCManager {
|
||||
this.rateLimiterId = null;
|
||||
this.intentionalDisconnect = false;
|
||||
this.lastCleanupTime = Date.now();
|
||||
|
||||
// Флаги для предотвращения дублирования системных сообщений
|
||||
this.lastSecurityLevelNotification = null;
|
||||
this.verificationNotificationSent = false;
|
||||
this.verificationInitiationSent = false;
|
||||
this.disconnectNotificationSent = false;
|
||||
this.reconnectionFailedNotificationSent = false;
|
||||
this.peerDisconnectNotificationSent = false;
|
||||
this.connectionClosedNotificationSent = false;
|
||||
this.fakeTrafficDisabledNotificationSent = false;
|
||||
this.advancedFeaturesDisabledNotificationSent = false;
|
||||
this.securityUpgradeNotificationSent = false;
|
||||
this.lastSecurityUpgradeStage = null;
|
||||
this.securityCalculationNotificationSent = false;
|
||||
this.lastSecurityCalculationLevel = null;
|
||||
|
||||
// File transfer integration
|
||||
this.fileTransferSystem = null;
|
||||
this.onFileProgress = null;
|
||||
@@ -173,15 +189,16 @@ class EnhancedSecureWebRTCManager {
|
||||
try {
|
||||
console.log('🔧 Initializing Enhanced Secure File Transfer system...');
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Более мягкая проверка готовности
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Убедимся что dataChannel готов
|
||||
if (!this.dataChannel || this.dataChannel.readyState !== 'open') {
|
||||
console.warn('⚠️ Data channel not open, deferring file transfer initialization');
|
||||
// Попробуем позже, не бросаем ошибку
|
||||
setTimeout(() => {
|
||||
if (this.dataChannel && this.dataChannel.readyState === 'open') {
|
||||
|
||||
if (this.dataChannel) {
|
||||
this.dataChannel.addEventListener('open', () => {
|
||||
console.log('🔄 DataChannel opened, initializing file transfer...');
|
||||
this.initializeFileTransfer();
|
||||
}
|
||||
}, 1000);
|
||||
}, { once: true }); // Используем once: true чтобы обработчик сработал только один раз
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,54 +210,30 @@ class EnhancedSecureWebRTCManager {
|
||||
}
|
||||
|
||||
this.fileTransferSystem = new EnhancedSecureFileTransfer(
|
||||
this, // Pass WebRTC manager reference
|
||||
|
||||
// Progress callback
|
||||
(progress) => {
|
||||
if (this.onFileProgress) {
|
||||
this.onFileProgress(progress);
|
||||
}
|
||||
|
||||
const progressMsg = `📁 ${progress.fileName || 'Unknown file'}: ${progress.progress.toFixed(1)}% (${progress.status})`;
|
||||
if (this.onMessage) {
|
||||
this.onMessage(progressMsg, 'system');
|
||||
}
|
||||
},
|
||||
|
||||
// Completion callback
|
||||
(result) => {
|
||||
const completionMsg = `✅ File sent: ${result.fileName} (${(result.transferTime / 1000).toFixed(1)}s)`;
|
||||
if (this.onMessage) {
|
||||
this.onMessage(completionMsg, 'system');
|
||||
}
|
||||
},
|
||||
|
||||
// Error callback
|
||||
this, // WebRTC manager reference
|
||||
null, // Progress callback - отключен для чата
|
||||
null, // Completion callback - отключен для чата
|
||||
(error) => {
|
||||
// Error callback - только критические ошибки
|
||||
console.error('File transfer critical error:', error);
|
||||
if (this.onFileError) {
|
||||
this.onFileError(error);
|
||||
}
|
||||
|
||||
if (this.onMessage) {
|
||||
this.onMessage(`❌ File transfer error: ${error}`, 'system');
|
||||
}
|
||||
},
|
||||
|
||||
// File received callback
|
||||
(fileData) => {
|
||||
// File received callback
|
||||
if (this.onFileReceived) {
|
||||
this.onFileReceived(fileData);
|
||||
}
|
||||
|
||||
const receivedMsg = `📥 File received: ${fileData.fileName} (${(fileData.fileSize / 1024 / 1024).toFixed(2)} MB)`;
|
||||
if (this.onMessage) {
|
||||
this.onMessage(receivedMsg, 'system');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
window.FILE_TRANSFER_ACTIVE = true;
|
||||
window.fileTransferSystem = this.fileTransferSystem;
|
||||
|
||||
console.log('✅ Enhanced Secure File Transfer system initialized successfully');
|
||||
|
||||
|
||||
// КРИТИЧЕСКОЕ ДОБАВЛЕНИЕ: Проверяем что система готова
|
||||
const status = this.fileTransferSystem.getSystemStatus();
|
||||
console.log('🔍 File transfer system status after init:', status);
|
||||
@@ -248,11 +241,8 @@ class EnhancedSecureWebRTCManager {
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to initialize file transfer system:', error);
|
||||
this.fileTransferSystem = null;
|
||||
|
||||
// Не выбрасываем ошибку, чтобы не нарушить основное соединение
|
||||
if (this.onMessage) {
|
||||
this.onMessage('⚠️ File transfer system initialization failed. File transfers may not work.', 'system');
|
||||
}
|
||||
window.FILE_TRANSFER_ACTIVE = false;
|
||||
window.fileTransferSystem = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,9 +380,83 @@ class EnhancedSecureWebRTCManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
deliverMessageToUI(message, type = 'user') {
|
||||
try {
|
||||
// Фильтруем file transfer и системные сообщения
|
||||
if (typeof message === 'object' && message.type) {
|
||||
const blockedTypes = [
|
||||
'file_transfer_start',
|
||||
'file_transfer_response',
|
||||
'file_chunk',
|
||||
'chunk_confirmation',
|
||||
'file_transfer_complete',
|
||||
'file_transfer_error',
|
||||
'heartbeat',
|
||||
'verification',
|
||||
'verification_response',
|
||||
'peer_disconnect',
|
||||
'key_rotation_signal',
|
||||
'key_rotation_ready',
|
||||
'security_upgrade'
|
||||
];
|
||||
if (blockedTypes.includes(message.type)) {
|
||||
if (window.DEBUG_MODE) {
|
||||
console.log(`🛑 Blocked system/file message from UI: ${message.type}`);
|
||||
}
|
||||
return; // не показываем в чате
|
||||
}
|
||||
}
|
||||
|
||||
// Дополнительная проверка для строковых сообщений, содержащих JSON
|
||||
if (typeof message === 'string' && message.trim().startsWith('{')) {
|
||||
try {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
if (parsedMessage.type) {
|
||||
const blockedTypes = [
|
||||
'file_transfer_start',
|
||||
'file_transfer_response',
|
||||
'file_chunk',
|
||||
'chunk_confirmation',
|
||||
'file_transfer_complete',
|
||||
'file_transfer_error',
|
||||
'heartbeat',
|
||||
'verification',
|
||||
'verification_response',
|
||||
'peer_disconnect',
|
||||
'key_rotation_signal',
|
||||
'key_rotation_ready',
|
||||
'security_upgrade'
|
||||
];
|
||||
if (blockedTypes.includes(parsedMessage.type)) {
|
||||
if (window.DEBUG_MODE) {
|
||||
console.log(`🛑 Blocked system/file message from UI (string): ${parsedMessage.type}`);
|
||||
}
|
||||
return; // не показываем в чате
|
||||
}
|
||||
}
|
||||
} catch (parseError) {
|
||||
// Не JSON - это нормально для обычных текстовых сообщений
|
||||
}
|
||||
}
|
||||
|
||||
if (this.onMessage) {
|
||||
this.onMessage(message, type);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('❌ Failed to deliver message to UI:', err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Security Level Notification
|
||||
notifySecurityLevel() {
|
||||
// Проверяем, не было ли уже отправлено сообщение о текущем уровне безопасности
|
||||
if (this.lastSecurityLevelNotification === this.currentSecurityLevel) {
|
||||
return; // Избегаем дублирования
|
||||
}
|
||||
|
||||
this.lastSecurityLevelNotification = this.currentSecurityLevel;
|
||||
|
||||
const levelMessages = {
|
||||
'basic': '🔒 Basic Security Active - Demo session with essential protection',
|
||||
'enhanced': '🔐 Enhanced Security Active - Paid session with advanced protection',
|
||||
@@ -402,7 +466,7 @@ class EnhancedSecureWebRTCManager {
|
||||
const message = levelMessages[this.currentSecurityLevel] || levelMessages['basic'];
|
||||
|
||||
if (this.onMessage) {
|
||||
this.onMessage(message, 'system');
|
||||
this.deliverMessageToUI(message, 'system');
|
||||
}
|
||||
|
||||
// Showing details of functions for paid sessions
|
||||
@@ -412,7 +476,7 @@ class EnhancedSecureWebRTCManager {
|
||||
.map(([key]) => key.replace('has', '').replace(/([A-Z])/g, ' $1').trim().toLowerCase())
|
||||
.slice(0, 5);
|
||||
|
||||
this.onMessage(`🔧 Active: ${activeFeatures.join(', ')}...`, 'system');
|
||||
this.deliverMessageToUI(`🔧 Active: ${activeFeatures.join(', ')}...`, 'system');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,8 +790,12 @@ emergencyDisableAdvancedFeatures() {
|
||||
|
||||
console.log('✅ Advanced features disabled, keeping basic encryption');
|
||||
|
||||
if (this.onMessage) {
|
||||
this.onMessage('🚨 Advanced security features temporarily disabled due to compatibility issues', 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о отключении расширенных функций
|
||||
if (!this.advancedFeaturesDisabledNotificationSent) {
|
||||
this.advancedFeaturesDisabledNotificationSent = true;
|
||||
if (this.onMessage) {
|
||||
this.deliverMessageToUI('🚨 Advanced security features temporarily disabled due to compatibility issues', 'system');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,8 +863,12 @@ emergencyDisableFakeTraffic() {
|
||||
console.log('✅ Fake traffic disabled');
|
||||
}
|
||||
|
||||
if (this.onMessage) {
|
||||
this.onMessage('🚨 Fake traffic emergency disabled', 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о отключении fake traffic
|
||||
if (!this.fakeTrafficDisabledNotificationSent) {
|
||||
this.fakeTrafficDisabledNotificationSent = true;
|
||||
if (this.onMessage) {
|
||||
this.deliverMessageToUI('🚨 Fake traffic emergency disabled', 'system');
|
||||
}
|
||||
}
|
||||
}
|
||||
// ============================================
|
||||
@@ -1441,12 +1513,20 @@ async processOrderedPackets() {
|
||||
return 'FAKE_MESSAGE_FILTERED';
|
||||
}
|
||||
|
||||
// System messages
|
||||
// System messages - НЕ возвращаем для повторной обработки
|
||||
if (jsonData.type && ['heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'key_rotation_signal', 'key_rotation_ready', 'security_upgrade'].includes(jsonData.type)) {
|
||||
if (window.DEBUG_MODE) {
|
||||
console.log('🔧 System message detected:', jsonData.type);
|
||||
console.log('🔧 System message detected, blocking from chat:', jsonData.type);
|
||||
}
|
||||
return data;
|
||||
return 'SYSTEM_MESSAGE_FILTERED';
|
||||
}
|
||||
|
||||
// File transfer messages - НЕ возвращаем для отображения
|
||||
if (jsonData.type && ['file_transfer_start', 'file_transfer_response', 'file_chunk', 'chunk_confirmation', 'file_transfer_complete', 'file_transfer_error'].includes(jsonData.type)) {
|
||||
if (window.DEBUG_MODE) {
|
||||
console.log('📁 File transfer message detected, blocking from chat:', jsonData.type);
|
||||
}
|
||||
return 'FILE_MESSAGE_FILTERED';
|
||||
}
|
||||
|
||||
// Regular text messages - extract the actual message text
|
||||
@@ -1524,7 +1604,7 @@ async processOrderedPackets() {
|
||||
}
|
||||
|
||||
// If it's not a special type, return the original data for display
|
||||
if (!jsonData.type || (jsonData.type !== 'fake' && !['heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'key_rotation_signal', 'key_rotation_ready', 'enhanced_message', 'security_upgrade'].includes(jsonData.type))) {
|
||||
if (!jsonData.type || (jsonData.type !== 'fake' && !['heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'key_rotation_signal', 'key_rotation_ready', 'enhanced_message', 'security_upgrade', 'file_transfer_start', 'file_transfer_response', 'file_chunk', 'chunk_confirmation', 'file_transfer_complete', 'file_transfer_error'].includes(jsonData.type))) {
|
||||
if (window.DEBUG_MODE) {
|
||||
console.log('📝 Regular message detected, returning for display');
|
||||
}
|
||||
@@ -1821,7 +1901,26 @@ async processOrderedPackets() {
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Ранняя проверка на файловые сообщения
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const fileMessageTypes = [
|
||||
'file_transfer_start',
|
||||
'file_transfer_response',
|
||||
'file_chunk',
|
||||
'chunk_confirmation',
|
||||
'file_transfer_complete',
|
||||
'file_transfer_error'
|
||||
];
|
||||
|
||||
if (parsed.type && fileMessageTypes.includes(parsed.type)) {
|
||||
console.log('📁 File message detected in processMessage:', parsed.type);
|
||||
|
||||
// Передаем в файловую систему
|
||||
if (window.fileTransferSystem && window.fileTransferSystem.handleFileMessage) {
|
||||
await window.fileTransferSystem.handleFileMessage(parsed);
|
||||
}
|
||||
return; // ВАЖНО: Выходим после обработки
|
||||
}
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Обработка файловых сообщений
|
||||
if (parsed.type && parsed.type.startsWith('file_')) {
|
||||
@@ -1865,14 +1964,13 @@ async processOrderedPackets() {
|
||||
if (parsed.type === 'message') {
|
||||
console.log('📝 Regular user message detected in processMessage');
|
||||
if (this.onMessage && parsed.data) {
|
||||
this.onMessage(parsed.data, 'received');
|
||||
this.deliverMessageToUI(parsed.data, 'received');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Системные сообщения
|
||||
if (parsed.type && ['heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'security_upgrade'].includes(parsed.type)) {
|
||||
console.log('🔧 System message in processMessage:', parsed.type);
|
||||
this.handleSystemMessage(parsed);
|
||||
return;
|
||||
}
|
||||
@@ -1884,23 +1982,22 @@ async processOrderedPackets() {
|
||||
}
|
||||
|
||||
} catch (jsonError) {
|
||||
// Не JSON - обрабатываем как текст
|
||||
console.log('📄 Non-JSON string message in processMessage');
|
||||
if (this.onMessage) {
|
||||
this.onMessage(data, 'received');
|
||||
// Не JSON - обрабатываем как текст
|
||||
if (this.onMessage) {
|
||||
this.deliverMessageToUI(data, 'received');
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Если дошли сюда - применяем security layers
|
||||
const originalData = await this.removeSecurityLayers(data);
|
||||
|
||||
if (originalData === 'FAKE_MESSAGE_FILTERED') {
|
||||
console.log('🎭 Fake message successfully filtered in processMessage');
|
||||
|
||||
if (originalData === 'FAKE_MESSAGE_FILTERED' || originalData === 'FILE_MESSAGE_FILTERED' || originalData === 'SYSTEM_MESSAGE_FILTERED') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!originalData) {
|
||||
console.warn('⚠️ No data returned from removeSecurityLayers');
|
||||
return;
|
||||
@@ -1940,7 +2037,7 @@ async processOrderedPackets() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Финальная проверка на fake сообщения
|
||||
// Финальная проверка на fake сообщения и файловые сообщения
|
||||
if (messageText && messageText.trim().startsWith('{')) {
|
||||
try {
|
||||
const finalCheck = JSON.parse(messageText);
|
||||
@@ -1948,6 +2045,12 @@ async processOrderedPackets() {
|
||||
console.log(`🎭 Final fake message check blocked: ${finalCheck.pattern}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Дополнительная проверка на файловые и системные сообщения
|
||||
if (finalCheck.type && ['file_transfer_start', 'file_transfer_response', 'file_chunk', 'chunk_confirmation', 'file_transfer_complete', 'file_transfer_error', 'heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'key_rotation_signal', 'key_rotation_ready', 'security_upgrade'].includes(finalCheck.type)) {
|
||||
console.log(`📁 Final system/file message check blocked: ${finalCheck.type}`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// Не JSON - это нормально для обычных текстовых сообщений
|
||||
}
|
||||
@@ -1956,7 +2059,7 @@ async processOrderedPackets() {
|
||||
// Отправляем сообщение пользователю
|
||||
if (this.onMessage && messageText) {
|
||||
console.log('📤 Calling message handler with:', messageText.substring(0, 100));
|
||||
this.onMessage(messageText, 'received');
|
||||
this.deliverMessageToUI(messageText, 'user');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -2035,10 +2138,8 @@ handleSystemMessage(message) {
|
||||
break;
|
||||
case 'security_upgrade':
|
||||
console.log('🔒 Security upgrade notification received:', message);
|
||||
// Display security upgrade message to user
|
||||
if (this.onMessage && message.message) {
|
||||
this.onMessage(message.message, 'system');
|
||||
}
|
||||
// Security upgrade messages are handled internally, not displayed to user
|
||||
// to prevent duplicate system messages
|
||||
break;
|
||||
default:
|
||||
console.log('🔧 Unknown system message type:', message.type);
|
||||
@@ -2169,9 +2270,15 @@ handleSystemMessage(message) {
|
||||
|
||||
const message = `🔒 Security upgraded to Stage ${stage}: ${stageNames[stage]}`;
|
||||
|
||||
// Notify local UI via onMessage
|
||||
if (this.onMessage) {
|
||||
this.onMessage(message, 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о повышении безопасности
|
||||
if (!this.securityUpgradeNotificationSent || this.lastSecurityUpgradeStage !== stage) {
|
||||
this.securityUpgradeNotificationSent = true;
|
||||
this.lastSecurityUpgradeStage = stage;
|
||||
|
||||
// Notify local UI via onMessage
|
||||
if (this.onMessage) {
|
||||
this.deliverMessageToUI(message, 'system');
|
||||
}
|
||||
}
|
||||
|
||||
// Send security upgrade notification to peer via WebRTC
|
||||
@@ -2247,8 +2354,14 @@ handleSystemMessage(message) {
|
||||
}));
|
||||
|
||||
if (securityData.isRealData && this.onMessage) {
|
||||
const message = `🔒 Security Level: ${securityData.level} (${securityData.score}%) - ${securityData.passedChecks}/${securityData.totalChecks} checks passed`;
|
||||
this.onMessage(message, 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о расчете безопасности
|
||||
if (!this.securityCalculationNotificationSent || this.lastSecurityCalculationLevel !== securityData.level) {
|
||||
this.securityCalculationNotificationSent = true;
|
||||
this.lastSecurityCalculationLevel = securityData.level;
|
||||
|
||||
const message = `🔒 Security Level: ${securityData.level} (${securityData.score}%) - ${securityData.passedChecks}/${securityData.totalChecks} checks passed`;
|
||||
this.deliverMessageToUI(message, 'system');
|
||||
}
|
||||
}
|
||||
|
||||
return securityData;
|
||||
@@ -2630,10 +2743,20 @@ handleSystemMessage(message) {
|
||||
this.dataChannel.onclose = () => {
|
||||
if (!this.intentionalDisconnect) {
|
||||
this.onStatusChange('disconnected');
|
||||
this.onMessage('🔌 Enhanced secure connection closed. Check connection status.', 'system');
|
||||
|
||||
// Проверяем, не было ли уже отправлено сообщение о закрытии соединения
|
||||
if (!this.connectionClosedNotificationSent) {
|
||||
this.connectionClosedNotificationSent = true;
|
||||
this.deliverMessageToUI('🔌 Enhanced secure connection closed. Check connection status.', 'system');
|
||||
}
|
||||
} else {
|
||||
this.onStatusChange('disconnected');
|
||||
this.onMessage('🔌 Enhanced secure connection closed', 'system');
|
||||
|
||||
// Проверяем, не было ли уже отправлено сообщение о закрытии соединения
|
||||
if (!this.connectionClosedNotificationSent) {
|
||||
this.connectionClosedNotificationSent = true;
|
||||
this.deliverMessageToUI('🔌 Enhanced secure connection closed', 'system');
|
||||
}
|
||||
}
|
||||
|
||||
this.stopHeartbeat();
|
||||
@@ -2643,97 +2766,87 @@ handleSystemMessage(message) {
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ ОБРАБОТКИ СООБЩЕНИЙ
|
||||
this.dataChannel.onmessage = async (event) => {
|
||||
try {
|
||||
console.log('📨 Raw message received:', {
|
||||
dataType: typeof event.data,
|
||||
dataLength: event.data?.length || 0,
|
||||
firstChars: typeof event.data === 'string' ? event.data.substring(0, 100) : 'not string'
|
||||
});
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Улучшенная проверка на JSON
|
||||
if (typeof event.data === 'string') {
|
||||
let parsed;
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Проверка файловых сообщений
|
||||
if (window.FILE_TRANSFER_ACTIVE && typeof event.data === 'string') {
|
||||
try {
|
||||
parsed = JSON.parse(event.data);
|
||||
} catch (jsonError) {
|
||||
console.warn('⚠️ Received non-JSON string message:', event.data.substring(0, 50));
|
||||
// Обрабатываем как обычное текстовое сообщение
|
||||
if (this.onMessage) {
|
||||
this.onMessage(event.data, 'received');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed.type && parsed.type.startsWith('file_')) {
|
||||
console.log('📁 FILE MESSAGE DETECTED:', parsed.type);
|
||||
// НЕМЕДЛЕННО передаем в processMessage для обработки
|
||||
await this.processMessage(event.data);
|
||||
return;
|
||||
}
|
||||
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Проверяем тип сообщения
|
||||
if (parsed.type === 'message') {
|
||||
console.log('🎯 USER MESSAGE DETECTED:', {
|
||||
type: parsed.type,
|
||||
data: parsed.data?.substring(0, 50) || 'no data',
|
||||
timestamp: parsed.timestamp,
|
||||
isInitiator: this.isInitiator
|
||||
});
|
||||
const parsed = JSON.parse(event.data);
|
||||
|
||||
// Обрабатываем пользовательское сообщение напрямую
|
||||
if (this.onMessage && parsed.data) {
|
||||
this.onMessage(parsed.data, 'received');
|
||||
// Список типов файловых сообщений
|
||||
const fileMessageTypes = [
|
||||
'file_transfer_start',
|
||||
'file_transfer_response',
|
||||
'file_chunk',
|
||||
'chunk_confirmation',
|
||||
'file_transfer_complete',
|
||||
'file_transfer_error'
|
||||
];
|
||||
|
||||
if (parsed.type && fileMessageTypes.includes(parsed.type)) {
|
||||
console.log('🚫 File message blocked at WebRTC level:', parsed.type);
|
||||
|
||||
// Передаем в файловую систему если она есть
|
||||
if (window.fileTransferSystem && window.fileTransferSystem.handleFileMessage) {
|
||||
await window.fileTransferSystem.handleFileMessage(parsed);
|
||||
}
|
||||
return; // НЕ обрабатываем дальше
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Системные сообщения
|
||||
if (parsed.type && ['heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'security_upgrade'].includes(parsed.type)) {
|
||||
console.log('🔧 SYSTEM MESSAGE DETECTED:', parsed.type);
|
||||
await this.processMessage(event.data);
|
||||
return;
|
||||
} catch (jsonError) {
|
||||
// Не JSON - продолжаем обычную обработку
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем все остальные сообщения через общий процессор
|
||||
await this.processMessage(event.data);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to process message in onmessage:', error);
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Fallback обработка
|
||||
try {
|
||||
if (typeof event.data === 'string') {
|
||||
const fallbackParsed = JSON.parse(event.data);
|
||||
|
||||
// Обработка обычных сообщений
|
||||
if (typeof event.data === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(event.data);
|
||||
|
||||
// Обработка основных типов сообщений как fallback
|
||||
if (fallbackParsed.type === 'message' && fallbackParsed.data) {
|
||||
console.log('🔄 Fallback: Processing user message');
|
||||
// Обычные пользовательские сообщения
|
||||
if (parsed.type === 'message' && parsed.data) {
|
||||
if (this.onMessage) {
|
||||
this.onMessage(fallbackParsed.data, 'received');
|
||||
this.deliverMessageToUI(parsed.data, 'received');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallbackParsed.type === 'heartbeat') {
|
||||
console.log('🔄 Fallback: Processing heartbeat');
|
||||
this.handleHeartbeat();
|
||||
// Системные сообщения
|
||||
if (parsed.type && ['heartbeat', 'verification', 'verification_response', 'peer_disconnect', 'security_upgrade'].includes(parsed.type)) {
|
||||
await this.processMessage(event.data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
console.error('❌ Fallback message processing also failed:', fallbackError);
|
||||
|
||||
// Последний fallback - обработка как текст если это строка
|
||||
if (typeof event.data === 'string' && this.onMessage) {
|
||||
this.onMessage(`[Received]: ${event.data}`, 'received');
|
||||
} catch (jsonError) {
|
||||
// Обрабатываем как обычное текстовое сообщение
|
||||
if (this.onMessage) {
|
||||
this.deliverMessageToUI(event.data, 'received');
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем все остальные сообщения
|
||||
await this.processMessage(event.data);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to process message in onmessage:', error);
|
||||
}
|
||||
};
|
||||
}
|
||||
async createSecureOffer() {
|
||||
try {
|
||||
// Сброс флагов уведомлений для нового соединения
|
||||
this.lastSecurityLevelNotification = null;
|
||||
this.verificationNotificationSent = false;
|
||||
this.verificationInitiationSent = false;
|
||||
this.disconnectNotificationSent = false;
|
||||
this.reconnectionFailedNotificationSent = false;
|
||||
this.peerDisconnectNotificationSent = false;
|
||||
this.connectionClosedNotificationSent = false;
|
||||
this.fakeTrafficDisabledNotificationSent = false;
|
||||
this.advancedFeaturesDisabledNotificationSent = false;
|
||||
this.securityUpgradeNotificationSent = false;
|
||||
this.lastSecurityUpgradeStage = null;
|
||||
this.securityCalculationNotificationSent = false;
|
||||
this.lastSecurityCalculationLevel = null;
|
||||
|
||||
// Check rate limiting
|
||||
if (!window.EnhancedSecureCryptoUtils.rateLimiter.checkConnectionRate(this.rateLimiterId)) {
|
||||
throw new Error('Connection rate limit exceeded. Please wait before trying again.');
|
||||
@@ -2857,6 +2970,21 @@ handleSystemMessage(message) {
|
||||
|
||||
async createSecureAnswer(offerData) {
|
||||
try {
|
||||
// Сброс флагов уведомлений для нового соединения
|
||||
this.lastSecurityLevelNotification = null;
|
||||
this.verificationNotificationSent = false;
|
||||
this.verificationInitiationSent = false;
|
||||
this.disconnectNotificationSent = false;
|
||||
this.reconnectionFailedNotificationSent = false;
|
||||
this.peerDisconnectNotificationSent = false;
|
||||
this.connectionClosedNotificationSent = false;
|
||||
this.fakeTrafficDisabledNotificationSent = false;
|
||||
this.advancedFeaturesDisabledNotificationSent = false;
|
||||
this.securityUpgradeNotificationSent = false;
|
||||
this.lastSecurityUpgradeStage = null;
|
||||
this.securityCalculationNotificationSent = false;
|
||||
this.lastSecurityCalculationLevel = null;
|
||||
|
||||
window.EnhancedSecureCryptoUtils.secureLog.log('info', 'Starting createSecureAnswer', {
|
||||
hasOfferData: !!offerData,
|
||||
offerType: offerData?.type,
|
||||
@@ -3331,8 +3459,11 @@ handleSystemMessage(message) {
|
||||
|
||||
initiateVerification() {
|
||||
if (this.isInitiator) {
|
||||
// Initiator waits for verification confirmation
|
||||
this.onMessage('🔐 Confirm the security code with your peer to complete the connection', 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о подтверждении верификации
|
||||
if (!this.verificationInitiationSent) {
|
||||
this.verificationInitiationSent = true;
|
||||
this.deliverMessageToUI('🔐 Confirm the security code with your peer to complete the connection', 'system');
|
||||
}
|
||||
} else {
|
||||
// Responder confirms verification automatically if codes match
|
||||
this.confirmVerification();
|
||||
@@ -3352,11 +3483,17 @@ handleSystemMessage(message) {
|
||||
this.dataChannel.send(JSON.stringify(verificationPayload));
|
||||
this.isVerified = true;
|
||||
this.onStatusChange('connected');
|
||||
this.onMessage('✅ Verification successful. The channel is now secure!', 'system');
|
||||
|
||||
// Проверяем, не было ли уже отправлено сообщение о верификации
|
||||
if (!this.verificationNotificationSent) {
|
||||
this.verificationNotificationSent = true;
|
||||
this.deliverMessageToUI('✅ Verification successful. The channel is now secure!', 'system');
|
||||
}
|
||||
|
||||
this.processMessageQueue();
|
||||
} catch (error) {
|
||||
console.error('Verification failed:', error);
|
||||
this.onMessage('❌ Verification failed', 'system');
|
||||
this.deliverMessageToUI('❌ Verification failed', 'system');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3372,10 +3509,16 @@ handleSystemMessage(message) {
|
||||
this.dataChannel.send(JSON.stringify(responsePayload));
|
||||
this.isVerified = true;
|
||||
this.onStatusChange('connected');
|
||||
this.onMessage('✅ Verification successful. The channel is now secure!', 'system');
|
||||
|
||||
// Проверяем, не было ли уже отправлено сообщение о верификации
|
||||
if (!this.verificationNotificationSent) {
|
||||
this.verificationNotificationSent = true;
|
||||
this.deliverMessageToUI('✅ Verification successful. The channel is now secure!', 'system');
|
||||
}
|
||||
|
||||
this.processMessageQueue();
|
||||
} else {
|
||||
this.onMessage('❌ Verification code mismatch! Possible MITM attack detected. Connection aborted for safety!', 'system');
|
||||
this.deliverMessageToUI('❌ Verification code mismatch! Possible MITM attack detected. Connection aborted for safety!', 'system');
|
||||
this.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -3384,10 +3527,16 @@ handleSystemMessage(message) {
|
||||
if (data.verified) {
|
||||
this.isVerified = true;
|
||||
this.onStatusChange('connected');
|
||||
this.onMessage('✅ Verification successful. The channel is now secure.!', 'system');
|
||||
|
||||
// Проверяем, не было ли уже отправлено сообщение о верификации
|
||||
if (!this.verificationNotificationSent) {
|
||||
this.verificationNotificationSent = true;
|
||||
this.deliverMessageToUI('✅ Verification successful. The channel is now secure!', 'system');
|
||||
}
|
||||
|
||||
this.processMessageQueue();
|
||||
} else {
|
||||
this.onMessage('❌ Verification failed!', 'system');
|
||||
this.deliverMessageToUI('❌ Verification failed!', 'system');
|
||||
this.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -3554,7 +3703,7 @@ handleSystemMessage(message) {
|
||||
};
|
||||
|
||||
this.dataChannel.send(JSON.stringify(payload));
|
||||
this.onMessage(sanitizedMessage, 'sent');
|
||||
this.deliverMessageToUI(sanitizedMessage, 'sent');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Enhanced message sending failed:', error);
|
||||
@@ -3675,7 +3824,12 @@ handleSystemMessage(message) {
|
||||
handleUnexpectedDisconnect() {
|
||||
this.sendDisconnectNotification();
|
||||
this.isVerified = false;
|
||||
this.onMessage('🔌 Connection lost. Attempting to reconnect...', 'system');
|
||||
|
||||
// Проверяем, не было ли уже отправлено сообщение о разрыве соединения
|
||||
if (!this.disconnectNotificationSent) {
|
||||
this.disconnectNotificationSent = true;
|
||||
this.deliverMessageToUI('🔌 Connection lost. Attempting to reconnect...', 'system');
|
||||
}
|
||||
|
||||
// Cleanup file transfer system on unexpected disconnect
|
||||
if (this.fileTransferSystem) {
|
||||
@@ -3734,7 +3888,11 @@ handleSystemMessage(message) {
|
||||
}
|
||||
|
||||
attemptReconnection() {
|
||||
this.onMessage('❌ Unable to reconnect. A new connection is required.', 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о неудачном переподключении
|
||||
if (!this.reconnectionFailedNotificationSent) {
|
||||
this.reconnectionFailedNotificationSent = true;
|
||||
this.deliverMessageToUI('❌ Unable to reconnect. A new connection is required.', 'system');
|
||||
}
|
||||
// Не вызываем cleanupConnection автоматически
|
||||
// чтобы не закрывать сессию при ошибках
|
||||
// this.cleanupConnection();
|
||||
@@ -3744,7 +3902,12 @@ handleSystemMessage(message) {
|
||||
const reason = data.reason || 'unknown';
|
||||
const reasonText = reason === 'user_disconnect' ? 'manually disconnected.' : 'connection lost.';
|
||||
|
||||
this.onMessage(`👋 Peer ${reasonText}`, 'system');
|
||||
// Проверяем, не было ли уже отправлено сообщение о разрыве соединения пира
|
||||
if (!this.peerDisconnectNotificationSent) {
|
||||
this.peerDisconnectNotificationSent = true;
|
||||
this.deliverMessageToUI(`👋 Peer ${reasonText}`, 'system');
|
||||
}
|
||||
|
||||
this.onStatusChange('peer_disconnected');
|
||||
|
||||
this.intentionalDisconnect = false;
|
||||
|
||||
@@ -11,6 +11,10 @@ class EnhancedSecureFileTransfer {
|
||||
throw new Error('webrtcManager is required for EnhancedSecureFileTransfer');
|
||||
}
|
||||
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Устанавливаем глобальный флаг
|
||||
window.FILE_TRANSFER_ACTIVE = true;
|
||||
window.fileTransferSystem = this;
|
||||
|
||||
console.log('🔍 Debug: webrtcManager in constructor:', {
|
||||
hasWebrtcManager: !!webrtcManager,
|
||||
webrtcManagerType: webrtcManager.constructor?.name,
|
||||
@@ -39,12 +43,176 @@ class EnhancedSecureFileTransfer {
|
||||
this.processedChunks = new Set(); // Prevent replay attacks
|
||||
this.transferNonces = new Map(); // fileId -> current nonce counter
|
||||
|
||||
// Initialize message handlers
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Регистрируем обработчик сообщений
|
||||
this.setupFileMessageHandlers();
|
||||
|
||||
console.log('🔒 Enhanced Secure File Transfer initialized');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ - ОБРАБОТКА СООБЩЕНИЙ
|
||||
// ============================================
|
||||
|
||||
setupFileMessageHandlers() {
|
||||
console.log('🔧 Setting up file message handlers');
|
||||
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Ждем готовности dataChannel
|
||||
if (!this.webrtcManager.dataChannel) {
|
||||
console.log('⏰ DataChannel not ready, deferring setup...');
|
||||
// Попытаемся настроить через небольшой интервал
|
||||
const setupRetry = setInterval(() => {
|
||||
if (this.webrtcManager.dataChannel) {
|
||||
clearInterval(setupRetry);
|
||||
console.log('🔄 DataChannel ready, setting up handlers...');
|
||||
this.setupMessageInterception();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Timeout для предотвращения бесконечного ожидания
|
||||
setTimeout(() => {
|
||||
clearInterval(setupRetry);
|
||||
console.warn('⚠️ DataChannel setup timeout');
|
||||
}, 5000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Если dataChannel уже готов, сразу настраиваем
|
||||
this.setupMessageInterception();
|
||||
|
||||
console.log('✅ File message handlers configured');
|
||||
}
|
||||
|
||||
// В методе setupMessageInterception(), замените весь метод на:
|
||||
setupMessageInterception() {
|
||||
try {
|
||||
if (!this.webrtcManager.dataChannel) {
|
||||
console.warn('⚠️ WebRTC manager data channel not available yet');
|
||||
return;
|
||||
}
|
||||
|
||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Глобальный флаг для блокировки файловых сообщений
|
||||
window.FILE_TRANSFER_ACTIVE = true;
|
||||
window.fileTransferSystem = this;
|
||||
|
||||
// 1. ПЕРЕХВАТ НА УРОВНЕ dataChannel.onmessage
|
||||
if (this.webrtcManager.dataChannel.onmessage) {
|
||||
this.originalOnMessage = this.webrtcManager.dataChannel.onmessage;
|
||||
console.log('💾 Original onmessage handler saved');
|
||||
}
|
||||
|
||||
this.webrtcManager.dataChannel.onmessage = async (event) => {
|
||||
try {
|
||||
// Проверяем файловые сообщения ПЕРВЫМИ
|
||||
if (typeof event.data === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(event.data);
|
||||
|
||||
if (this.isFileTransferMessage(parsed)) {
|
||||
console.log('🛑 FILE MESSAGE BLOCKED FROM CHAT:', parsed.type);
|
||||
await this.handleFileMessage(parsed);
|
||||
return; // КРИТИЧЕСКИ ВАЖНО: НЕ передаем дальше
|
||||
}
|
||||
} catch (parseError) {
|
||||
// Не JSON - передаем оригинальному обработчику
|
||||
}
|
||||
}
|
||||
|
||||
// Передаем обычные сообщения оригинальному обработчику
|
||||
if (this.originalOnMessage) {
|
||||
return this.originalOnMessage.call(this.webrtcManager.dataChannel, event);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error in file system message interception:', error);
|
||||
if (this.originalOnMessage) {
|
||||
return this.originalOnMessage.call(this.webrtcManager.dataChannel, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
console.log('✅ Message interception set up successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to set up message interception:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем, является ли сообщение файловым
|
||||
isFileTransferMessage(message) {
|
||||
if (!message || typeof message !== 'object' || !message.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fileMessageTypes = [
|
||||
'file_transfer_start',
|
||||
'file_transfer_response',
|
||||
'file_chunk',
|
||||
'chunk_confirmation',
|
||||
'file_transfer_complete',
|
||||
'file_transfer_error'
|
||||
];
|
||||
|
||||
const isFileMessage = fileMessageTypes.includes(message.type);
|
||||
|
||||
if (isFileMessage) {
|
||||
console.log(`🎯 CONFIRMED FILE MESSAGE: ${message.type} - WILL BE BLOCKED FROM CHAT`);
|
||||
}
|
||||
|
||||
return isFileMessage;
|
||||
}
|
||||
|
||||
// Обрабатываем файловые сообщения
|
||||
async handleFileMessage(message) {
|
||||
try {
|
||||
console.log(`🔄 Handling file message: ${message.type}`, {
|
||||
fileId: message.fileId,
|
||||
type: message.type
|
||||
});
|
||||
|
||||
switch (message.type) {
|
||||
case 'file_transfer_start':
|
||||
await this.handleFileTransferStart(message);
|
||||
break;
|
||||
|
||||
case 'file_transfer_response':
|
||||
this.handleTransferResponse(message);
|
||||
break;
|
||||
|
||||
case 'file_chunk':
|
||||
await this.handleFileChunk(message);
|
||||
break;
|
||||
|
||||
case 'chunk_confirmation':
|
||||
this.handleChunkConfirmation(message);
|
||||
break;
|
||||
|
||||
case 'file_transfer_complete':
|
||||
this.handleTransferComplete(message);
|
||||
break;
|
||||
|
||||
case 'file_transfer_error':
|
||||
this.handleTransferError(message);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn('⚠️ Unknown file message type:', message.type);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error handling file message:', error);
|
||||
|
||||
// Отправляем сообщение об ошибке
|
||||
if (message.fileId) {
|
||||
const errorMessage = {
|
||||
type: 'file_transfer_error',
|
||||
fileId: message.fileId,
|
||||
error: error.message,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
await this.sendSecureMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SIMPLIFIED KEY DERIVATION - USE SHARED DATA
|
||||
// ============================================
|
||||
@@ -281,17 +449,9 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
transferState.status = 'metadata_sent';
|
||||
|
||||
// Notify progress
|
||||
if (this.onProgress) {
|
||||
this.onProgress({
|
||||
fileId: transferState.fileId,
|
||||
fileName: transferState.file.name,
|
||||
progress: 5, // 5% for metadata sent
|
||||
status: 'metadata_sent',
|
||||
totalChunks: transferState.totalChunks,
|
||||
sentChunks: 0
|
||||
});
|
||||
}
|
||||
// ИСПРАВЛЕНИЕ: НЕ отправляем системные сообщения в чат
|
||||
// Только логируем
|
||||
console.log(`📁 File metadata sent: ${transferState.file.name} (5% progress)`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to send file metadata:', error);
|
||||
@@ -321,16 +481,9 @@ class EnhancedSecureFileTransfer {
|
||||
transferState.sentChunks++;
|
||||
const progress = Math.round((transferState.sentChunks / totalChunks) * 95) + 5; // 5-100%
|
||||
|
||||
if (this.onProgress) {
|
||||
this.onProgress({
|
||||
fileId: transferState.fileId,
|
||||
fileName: transferState.file.name,
|
||||
progress: progress,
|
||||
status: 'transmitting',
|
||||
totalChunks: totalChunks,
|
||||
sentChunks: transferState.sentChunks
|
||||
});
|
||||
}
|
||||
// ИСПРАВЛЕНИЕ: НЕ отправляем каждый чанк в чат
|
||||
// Только логируем
|
||||
console.log(`📤 Chunk sent ${transferState.sentChunks}/${totalChunks} (${progress}%)`);
|
||||
|
||||
// Small delay between chunks to prevent overwhelming
|
||||
if (chunkIndex < totalChunks - 1) {
|
||||
@@ -437,11 +590,6 @@ class EnhancedSecureFileTransfer {
|
||||
// MESSAGE HANDLERS
|
||||
// ============================================
|
||||
|
||||
setupFileMessageHandlers() {
|
||||
// This is now handled by WebRTC manager's processMessage method
|
||||
// No need to override onMessage here
|
||||
}
|
||||
|
||||
async handleFileTransferStart(metadata) {
|
||||
try {
|
||||
console.log('📥 Receiving file transfer:', metadata.fileName);
|
||||
@@ -493,17 +641,9 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
await this.sendSecureMessage(response);
|
||||
|
||||
// Notify progress
|
||||
if (this.onProgress) {
|
||||
this.onProgress({
|
||||
fileId: receivingState.fileId,
|
||||
fileName: receivingState.fileName,
|
||||
progress: 0,
|
||||
status: 'receiving',
|
||||
totalChunks: receivingState.totalChunks,
|
||||
receivedChunks: 0
|
||||
});
|
||||
}
|
||||
// ИСПРАВЛЕНИЕ: НЕ отправляем уведомления в чат
|
||||
// Только логируем
|
||||
console.log(`📥 Started receiving file: ${receivingState.fileName} (${(receivingState.fileSize / 1024 / 1024).toFixed(2)} MB)`);
|
||||
|
||||
// Process buffered chunks if any
|
||||
if (this.pendingChunks.has(metadata.fileId)) {
|
||||
@@ -592,17 +732,8 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
console.log(`📥 Received chunk ${chunkMessage.chunkIndex + 1}/${receivingState.totalChunks} (${progress}%)`);
|
||||
|
||||
// Notify progress
|
||||
if (this.onProgress) {
|
||||
this.onProgress({
|
||||
fileId: receivingState.fileId,
|
||||
fileName: receivingState.fileName,
|
||||
progress: progress,
|
||||
status: 'receiving',
|
||||
totalChunks: receivingState.totalChunks,
|
||||
receivedChunks: receivingState.receivedCount
|
||||
});
|
||||
}
|
||||
// ИСПРАВЛЕНИЕ: НЕ отправляем уведомления о прогрессе в чат
|
||||
// Только логируем
|
||||
|
||||
// Send chunk confirmation
|
||||
const confirmation = {
|
||||
@@ -783,6 +914,7 @@ class EnhancedSecureFileTransfer {
|
||||
try {
|
||||
const transferState = this.activeTransfers.get(confirmation.fileId);
|
||||
if (!transferState) {
|
||||
console.warn('⚠️ Received chunk confirmation for unknown transfer:', confirmation.fileId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -801,6 +933,7 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
const transferState = this.activeTransfers.get(completion.fileId);
|
||||
if (!transferState) {
|
||||
console.warn('⚠️ Received completion for unknown transfer:', completion.fileId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -979,6 +1112,29 @@ class EnhancedSecureFileTransfer {
|
||||
cleanup() {
|
||||
console.log('🧹 Cleaning up file transfer system');
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Очищаем глобальные флаги
|
||||
window.FILE_TRANSFER_ACTIVE = false;
|
||||
window.fileTransferSystem = null;
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Восстанавливаем ВСЕ перехваченные методы
|
||||
if (this.webrtcManager && this.webrtcManager.dataChannel && this.originalOnMessage) {
|
||||
console.log('🔄 Restoring original onmessage handler');
|
||||
this.webrtcManager.dataChannel.onmessage = this.originalOnMessage;
|
||||
this.originalOnMessage = null;
|
||||
}
|
||||
|
||||
if (this.webrtcManager && this.originalProcessMessage) {
|
||||
console.log('🔄 Restoring original processMessage handler');
|
||||
this.webrtcManager.processMessage = this.originalProcessMessage;
|
||||
this.originalProcessMessage = null;
|
||||
}
|
||||
|
||||
if (this.webrtcManager && this.originalRemoveSecurityLayers) {
|
||||
console.log('🔄 Restoring original removeSecurityLayers handler');
|
||||
this.webrtcManager.removeSecurityLayers = this.originalRemoveSecurityLayers;
|
||||
this.originalRemoveSecurityLayers = null;
|
||||
}
|
||||
|
||||
// Cleanup all active transfers
|
||||
for (const fileId of this.activeTransfers.keys()) {
|
||||
this.cleanupTransfer(fileId);
|
||||
@@ -996,6 +1152,8 @@ class EnhancedSecureFileTransfer {
|
||||
this.sessionKeys.clear();
|
||||
this.transferNonces.clear();
|
||||
this.processedChunks.clear();
|
||||
|
||||
console.log('✅ File transfer system cleaned up');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -1024,12 +1182,10 @@ class EnhancedSecureFileTransfer {
|
||||
try {
|
||||
console.log('🔍 Debug: Testing key derivation for:', fileId);
|
||||
|
||||
if (!this.webrtcManager.macKey) {
|
||||
throw new Error('MAC key not available');
|
||||
if (!this.webrtcManager.keyFingerprint || !this.webrtcManager.sessionSalt) {
|
||||
throw new Error('Session data not available');
|
||||
}
|
||||
|
||||
const salt = crypto.getRandomValues(new Uint8Array(32));
|
||||
|
||||
// Test sender derivation
|
||||
const senderResult = await this.deriveFileSessionKey(fileId);
|
||||
console.log('✅ Sender key derived successfully');
|
||||
@@ -1068,6 +1224,58 @@ class EnhancedSecureFileTransfer {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// АЛЬТЕРНАТИВНЫЙ МЕТОД ИНИЦИАЛИЗАЦИИ ОБРАБОТЧИКОВ
|
||||
// ============================================
|
||||
|
||||
// Если переопределение processMessage не работает,
|
||||
// используйте этот метод для явной регистрации обработчика
|
||||
registerWithWebRTCManager() {
|
||||
console.log('🔧 Registering file transfer handler with WebRTC manager');
|
||||
|
||||
if (!this.webrtcManager) {
|
||||
throw new Error('WebRTC manager not available');
|
||||
}
|
||||
|
||||
// Сохраняем ссылку на файловую систему в WebRTC менеджере
|
||||
this.webrtcManager.fileTransferSystem = this;
|
||||
|
||||
// КРИТИЧЕСКИ ВАЖНО: Устанавливаем обработчик файловых сообщений
|
||||
this.webrtcManager.setFileMessageHandler = (handler) => {
|
||||
this.webrtcManager._fileMessageHandler = handler;
|
||||
console.log('✅ File message handler registered in WebRTC manager');
|
||||
};
|
||||
|
||||
// Регистрируем наш обработчик
|
||||
this.webrtcManager.setFileMessageHandler((message) => {
|
||||
console.log('📁 File message via registered handler:', message.type);
|
||||
return this.handleFileMessage(message);
|
||||
});
|
||||
|
||||
console.log('✅ File transfer handler registered');
|
||||
}
|
||||
|
||||
// Метод для прямого вызова из WebRTC менеджера
|
||||
static createFileMessageFilter(fileTransferSystem) {
|
||||
return async (event) => {
|
||||
try {
|
||||
if (typeof event.data === 'string') {
|
||||
const parsed = JSON.parse(event.data);
|
||||
|
||||
if (fileTransferSystem.isFileTransferMessage(parsed)) {
|
||||
console.log('📁 File message filtered by static method:', parsed.type);
|
||||
await fileTransferSystem.handleFileMessage(parsed);
|
||||
return true; // Сообщение обработано
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Не файловое сообщение или ошибка парсинга
|
||||
}
|
||||
|
||||
return false; // Сообщение не обработано
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { EnhancedSecureFileTransfer };
|
||||
Reference in New Issue
Block a user