diff --git a/index.html b/index.html index c14fc21..15a7dcc 100644 --- a/index.html +++ b/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); }; diff --git a/src/components/ui/FileTransfer.jsx b/src/components/ui/FileTransfer.jsx index cb3b7e3..766c8f3 100644 --- a/src/components/ui/FileTransfer.jsx +++ b/src/components/ui/FileTransfer.jsx @@ -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)}%`) + ]) ]) ]) ) diff --git a/src/network/EnhancedSecureWebRTCManager.js b/src/network/EnhancedSecureWebRTCManager.js index 60f0d0f..c0ad0c8 100644 --- a/src/network/EnhancedSecureWebRTCManager.js +++ b/src/network/EnhancedSecureWebRTCManager.js @@ -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; diff --git a/src/transfer/EnhancedSecureFileTransfer.js b/src/transfer/EnhancedSecureFileTransfer.js index 8303de8..be6eb4e 100644 --- a/src/transfer/EnhancedSecureFileTransfer.js +++ b/src/transfer/EnhancedSecureFileTransfer.js @@ -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 }; \ No newline at end of file