diff options
| author | jack <212554440+jackjackbits@users.noreply.github.com> | 2025-08-12 11:33:08 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-12 11:33:08 +0200 |
| commit | 275f0ebaaf015db4d87e05f34cd53c5e7f3f3ab9 (patch) | |
| tree | 6e08f96cdd5d12e60a9687479d299ad750e1623c | |
| parent | 7a7c89e68993511510e693334313a027206c694a (diff) | |
Remove dead code and simplify codebase (#436)
* refactor: remove dead code and consolidate system messages
- Delete 3 unused functions in ShareViewController (36 lines)
- Extract addSystemMessage() helper to eliminate duplication (120+ lines)
- Remove 22 'let _ =' wasteful computations across multiple files
- Net reduction of 215 lines of dead/duplicate code
- Improves maintainability and reduces technical debt
* fix: remove remaining unused variables to eliminate compiler warnings
- Remove unused senderNoiseKey in ChatViewModel
- Remove unused lastSuccess variable in BluetoothMeshService
- Eliminates all compiler warnings related to unused values
* refactor: remove all dead legacy and migration code
- Remove unused migrateSession() functions (never called in production)
- NoiseSession.migrateSession() - 13 lines
- NoiseEncryptionService.migratePeerSession() - 18 lines
- Test for migration functionality - 17 lines
- Remove unnecessary keychain cleanup code (no legacy data existed)
- cleanupLegacyKeychainItems() - 62 lines
- aggressiveCleanupLegacyItems() - 72 lines
- resetCleanupFlag() - 4 lines
- Simplified panic mode to just use deleteAllKeychainData()
Total removed: 194 lines of dead/unnecessary code
Analysis revealed:
- KeychainManager introduced July 5, 2025
- Cleanup code added July 15, 2025 (10 days later)
- Legacy service names were never used in production
- Migration functions were incomplete implementation never called
- Peer ID rotation remains active (not legacy)
* Remove dead code and simplify codebase
- Remove unused BinaryEncodable protocol and BinaryMessageType enum
- Delete MockNoiseSession.swift (never used in tests)
- Remove all relay detection code (hardcoded to false)
- Removed isRelayConnected property from BitchatPeer
- Removed relayConnected case from ConnectionState enum
- Cleaned up relay-related UI indicators in ContentView
- Removed relay status checks from ChatViewModel
- Simplified peer connection logic by removing relay layer
Total: 169 lines removed across 5 files
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
| -rw-r--r-- | bitchat/Models/BitchatPeer.swift | 48 | ||||
| -rw-r--r-- | bitchat/Noise/NoiseSession.swift | 13 | ||||
| -rw-r--r-- | bitchat/Nostr/NostrProtocol.swift | 7 | ||||
| -rw-r--r-- | bitchat/Nostr/NostrRelayManager.swift | 7 | ||||
| -rw-r--r-- | bitchat/Protocols/BinaryEncodingUtils.swift | 15 | ||||
| -rw-r--r-- | bitchat/Services/BluetoothMeshService.swift | 13 | ||||
| -rw-r--r-- | bitchat/Services/KeychainManager.swift | 145 | ||||
| -rw-r--r-- | bitchat/Services/NoiseEncryptionService.swift | 18 | ||||
| -rw-r--r-- | bitchat/ViewModels/ChatViewModel.swift | 246 | ||||
| -rw-r--r-- | bitchat/Views/ContentView.swift | 15 | ||||
| -rw-r--r-- | bitchatShareExtension/ShareViewController.swift | 38 | ||||
| -rw-r--r-- | bitchatTests/Mocks/MockNoiseSession.swift | 101 | ||||
| -rw-r--r-- | bitchatTests/Noise/NoiseProtocolTests.swift | 17 |
13 files changed, 57 insertions, 626 deletions
diff --git a/bitchat/Models/BitchatPeer.swift b/bitchat/Models/BitchatPeer.swift index b8b9f21..566bc73 100644 --- a/bitchat/Models/BitchatPeer.swift +++ b/bitchat/Models/BitchatPeer.swift @@ -18,7 +18,6 @@ struct BitchatPeer: Identifiable, Equatable { // Connection state enum ConnectionState { case bluetoothConnected - case relayConnected // Connected via mesh relay (another peer) case nostrAvailable // Mutual favorite, reachable via Nostr case offline // Not connected via any transport } @@ -26,8 +25,6 @@ struct BitchatPeer: Identifiable, Equatable { var connectionState: ConnectionState { if isConnected { return .bluetoothConnected - } else if isRelayConnected { - return .relayConnected } else if favoriteStatus?.isMutual == true { // Mutual favorites can communicate via Nostr when offline return .nostrAvailable @@ -36,8 +33,6 @@ struct BitchatPeer: Identifiable, Equatable { } } - var isRelayConnected: Bool = false // Set by PeerManager based on session state - var isFavorite: Bool { favoriteStatus?.isFavorite ?? false } @@ -59,8 +54,6 @@ struct BitchatPeer: Identifiable, Equatable { switch connectionState { case .bluetoothConnected: return "📻" // Radio icon for mesh connection - case .relayConnected: - return "🔗" // Chain link for relay connection case .nostrAvailable: return "🌐" // Purple globe for Nostr case .offline: @@ -78,15 +71,13 @@ struct BitchatPeer: Identifiable, Equatable { noisePublicKey: Data, nickname: String, lastSeen: Date = Date(), - isConnected: Bool = false, - isRelayConnected: Bool = false + isConnected: Bool = false ) { self.id = id self.noisePublicKey = noisePublicKey self.nickname = nickname self.lastSeen = lastSeen self.isConnected = isConnected - self.isRelayConnected = isRelayConnected // Load favorite status - will be set later by the manager self.favoriteStatus = nil @@ -156,28 +147,15 @@ class PeerManager: ObservableObject { continue } - // Check if this peer is actually connected (not just known via relay) + // Check if this peer is actually connected let isConnected = meshService.isPeerConnected(peerID) - let isKnown = meshService.isPeerKnown(peerID) - // In a mesh network, a peer can only be relay-connected if: - // 1. We know about them (have received announce) - // 2. We're not directly connected - // 3. There are other peers that could relay (mesh peer count > 2) - // For now, disable relay detection until we have proper relay tracking - let isRelayConnected = false - - // Debug logging for relay connection detection - if isKnown && !isConnected { - SecureLogger.log("Peer \(nickname) (\(peerID)): isConnected=\(isConnected), isKnown=\(isKnown), isRelayConnected=\(isRelayConnected)", - category: SecureLogger.session, level: .debug) - } // Skip disconnected peers unless they're favorites (handled later) - if !isConnected && !isRelayConnected { + if !isConnected { continue } - if isConnected || isRelayConnected { + if isConnected { connectedNicknames.insert(nickname) } @@ -188,8 +166,7 @@ class PeerManager: ObservableObject { id: peerID, noisePublicKey: noiseKey, nickname: nickname, - isConnected: isConnected, - isRelayConnected: isRelayConnected + isConnected: isConnected ) // Set favorite status - check both by current noise key and by nickname if let favoriteStatus = favoritesService.getFavoriteStatus(for: noiseKey) { @@ -211,14 +188,14 @@ class PeerManager: ObservableObject { allPeers.append(peer) } - // Add offline favorites (only those not currently connected/relay-connected AND that we actively favorite) + // Add offline favorites (only those not currently connected AND that we actively favorite) for (favoriteKey, favorite) in favoritesService.favorites { let favoriteID = favorite.peerNoisePublicKey.hexEncodedString() - // Skip if this peer is already connected or relay-connected (by nickname) + // Skip if this peer is already connected (by nickname) if connectedNicknames.contains(favorite.peerNickname) { - SecureLogger.log(" - Skipping '\(favorite.peerNickname)' (key: \(favoriteKey.hexEncodedString())) - already connected/relay-connected", + SecureLogger.log(" - Skipping '\(favorite.peerNickname)' (key: \(favoriteKey.hexEncodedString())) - already connected", category: SecureLogger.session, level: .debug) continue } @@ -259,16 +236,12 @@ class PeerManager: ObservableObject { !(peer.displayName == "Unknown" && peer.favoriteStatus == nil) } - // Sort: Connected first (direct then relay), then favorites, then alphabetical + // Sort: Connected first, then favorites, then alphabetical allPeers.sort { lhs, rhs in // Direct connections first if lhs.isConnected != rhs.isConnected { return lhs.isConnected } - // Then relay connections - if lhs.isRelayConnected != rhs.isRelayConnected { - return lhs.isRelayConnected - } // Then favorites if lhs.isFavorite != rhs.isFavorite { return lhs.isFavorite @@ -321,13 +294,10 @@ class PeerManager: ObservableObject { // Log each peer's status for peer in allPeers { - // Use the actual statusIcon from the peer which accounts for relay connections let statusIcon: String switch peer.connectionState { case .bluetoothConnected: statusIcon = "🟢" - case .relayConnected: - statusIcon = "🔗" case .nostrAvailable: statusIcon = "🌐" case .offline: diff --git a/bitchat/Noise/NoiseSession.swift b/bitchat/Noise/NoiseSession.swift index bc327b5..005710a 100644 --- a/bitchat/Noise/NoiseSession.swift +++ b/bitchat/Noise/NoiseSession.swift @@ -296,19 +296,6 @@ class NoiseSessionManager { } } - func migrateSession(from oldPeerID: String, to newPeerID: String) { - managerQueue.sync(flags: .barrier) { - // Check if we have a session for the old peer ID - if let session = sessions[oldPeerID] { - // Move the session to the new peer ID - sessions[newPeerID] = session - _ = sessions.removeValue(forKey: oldPeerID) - - SecureLogger.log("Migrated Noise session from \(oldPeerID) to \(newPeerID)", category: SecureLogger.noise, level: .info) - } - } - } - func getEstablishedSessions() -> [String: NoiseSession] { return managerQueue.sync { return sessions.filter { $0.value.isEstablished() } diff --git a/bitchat/Nostr/NostrProtocol.swift b/bitchat/Nostr/NostrProtocol.swift index 068ce64..55880c5 100644 --- a/bitchat/Nostr/NostrProtocol.swift +++ b/bitchat/Nostr/NostrProtocol.swift @@ -37,7 +37,6 @@ struct NostrProtocol { // 2. Create ephemeral key for this message let ephemeralKey = try P256K.Schnorr.PrivateKey() - let _ = Data(ephemeralKey.xonly.bytes).hexEncodedString() // Created ephemeral key for seal // 3. Seal the rumor (encrypt to recipient) @@ -213,7 +212,6 @@ struct NostrProtocol { throw NostrError.invalidPublicKey } - let _ = Data(senderKey.xonly.bytes).hexEncodedString() // Encrypting message // Derive shared secret @@ -447,14 +445,11 @@ struct NostrProtocol { // Log with explicit UTC and local time for debugging let formatter = DateFormatter() + // Removed unnecessary date formatting operations formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" formatter.timeZone = TimeZone(abbreviation: "UTC") - let _ = formatter.string(from: now) - let _ = formatter.string(from: randomized) formatter.timeZone = TimeZone.current - let _ = formatter.string(from: now) - let _ = formatter.string(from: randomized) // Timestamp randomized for privacy diff --git a/bitchat/Nostr/NostrRelayManager.swift b/bitchat/Nostr/NostrRelayManager.swift index 351b95b..fa7c673 100644 --- a/bitchat/Nostr/NostrRelayManager.swift +++ b/bitchat/Nostr/NostrRelayManager.swift @@ -273,8 +273,7 @@ class NostrRelayManager: ObservableObject { } case "EOSE": - if array.count >= 2, - let _ = array[1] as? String { + if array.count >= 2 { // End of stored events } @@ -290,8 +289,8 @@ class NostrRelayManager: ObservableObject { } case "NOTICE": - if array.count >= 2, - let _ = array[1] as? String { + if array.count >= 2 { + // Server notice received } default: diff --git a/bitchat/Protocols/BinaryEncodingUtils.swift b/bitchat/Protocols/BinaryEncodingUtils.swift index f6d4ca9..575d207 100644 --- a/bitchat/Protocols/BinaryEncodingUtils.swift +++ b/bitchat/Protocols/BinaryEncodingUtils.swift @@ -221,18 +221,3 @@ extension Data { } } -// MARK: - Binary Message Protocol - -protocol BinaryEncodable { - func toBinaryData() -> Data - static func fromBinaryData(_ data: Data) -> Self? -} - -// MARK: - Message Type Registry - -enum BinaryMessageType: UInt8 { - case deliveryAck = 0x01 - case readReceipt = 0x02 - case noiseIdentityAnnouncement = 0x09 - case noiseMessage = 0x0A -}
\ No newline at end of file diff --git a/bitchat/Services/BluetoothMeshService.swift b/bitchat/Services/BluetoothMeshService.swift index 409a16e..3ae0f6f 100644 --- a/bitchat/Services/BluetoothMeshService.swift +++ b/bitchat/Services/BluetoothMeshService.swift @@ -705,7 +705,6 @@ class BluetoothMeshService: NSObject { // Update PeerSession if let session = peerSessions[peerID] { - let _ = session.isConnected session.updateBluetoothConnection(peripheral: mapping.peripheral, characteristic: nil) } else { // This is a truly new peer session @@ -2804,7 +2803,6 @@ class BluetoothMeshService: NSObject { // Check if we have a recent entry if let entry = protocolMessageDedup[key], !isExpired(entry) { - let _ = Date().timeIntervalSince(entry.sentAt) return true } @@ -3466,7 +3464,6 @@ class BluetoothMeshService: NSObject { // Bloom filter will be reset by timer, processedMessages is now bounded - // let _ = packet.senderID.hexEncodedString() // Note: We'll decode messages in the switch statement below, not here @@ -4548,7 +4545,6 @@ class BluetoothMeshService: NSObject { if !isPeerIDOurs(senderID) { // Check our current handshake state - let _ = handshakeCoordinator.getHandshakeState(for: senderID) // Processing handshake response // Process the response - this could be message 2 or message 3 in the XX pattern @@ -4734,7 +4730,6 @@ class BluetoothMeshService: NSObject { } } - let _ = Double(fragments.count - 1) * delayBetweenFragments } private func handleFragment(_ packet: BitchatPacket, from peerID: String) { @@ -5093,8 +5088,6 @@ extension BluetoothMeshService: CBCentralManagerDelegate { category: SecureLogger.session, level: .info) // Log current peripheral mappings - let _ = collectionsQueue.sync { peripheralMappings.count } - let _ = connectionPool.count peripheral.delegate = self peripheral.discoverServices([BluetoothMeshService.serviceUUID]) @@ -5282,8 +5275,6 @@ extension BluetoothMeshService: CBCentralManagerDelegate { // Time tracking removed - now in PeerSession // Time tracking removed - now in PeerSession // Keep lastSuccessfulMessageTime to validate session on reconnect - let lastSuccess = self.peerSessions[peerID]?.lastSuccessfulMessageTime ?? Date.distantPast - let _ = Date().timeIntervalSince(lastSuccess) // Keeping Noise session on disconnect } @@ -5500,7 +5491,6 @@ extension BluetoothMeshService: CBPeripheralDelegate { // Use the sender ID from the packet, not our local mapping which might still be a temp ID - let _ = connectedPeripherals.first(where: { $0.value == peripheral })?.key ?? "unknown" let packetSenderID = packet.senderID.hexEncodedString() // Log the packet type we received @@ -6611,8 +6601,6 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate { SecureLogger.logHandshake("processing \(isInitiation ? "init" : "response")", peerID: peerID, success: true) // Get current handshake state before processing - let _ = handshakeCoordinator.getHandshakeState(for: peerID) - let _ = noiseService.hasEstablishedSession(with: peerID) // Current handshake state check // Check for duplicate handshake messages @@ -7535,7 +7523,6 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate { self.pendingPrivateMessages[recipientPeerID] = [] } self.pendingPrivateMessages[recipientPeerID]?.append((content, recipientNickname, messageID ?? UUID().uuidString)) - let _ = self.pendingPrivateMessages[recipientPeerID]?.count ?? 0 // Queued private message } diff --git a/bitchat/Services/KeychainManager.swift b/bitchat/Services/KeychainManager.swift index 5eee249..e863392 100644 --- a/bitchat/Services/KeychainManager.swift +++ b/bitchat/Services/KeychainManager.swift @@ -17,73 +17,7 @@ class KeychainManager { private let service = "chat.bitchat" private let appGroup = "group.chat.bitchat" - private init() { - // Clean up legacy keychain items on first run - cleanupLegacyKeychainItems() - } - - private func cleanupLegacyKeychainItems() { - // Check if we've already done cleanup - let cleanupKey = "bitchat.keychain.cleanup.v2" - if UserDefaults.standard.bool(forKey: cleanupKey) { - return - } - - - // List of old service names to migrate from - let legacyServices = [ - "com.bitchat.passwords", - "com.bitchat.deviceidentity", - "com.bitchat.noise.identity", - "chat.bitchat.passwords", - "bitchat.keychain" - ] - - var migratedItems = 0 - - // Try to migrate identity keys - for oldService in legacyServices { - // Check for noise identity key - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: oldService, - kSecAttrAccount as String: "identity_noiseStaticKey", - kSecReturnData as String: true - ] - - var result: AnyObject? - let status = SecItemCopyMatching(query as CFDictionary, &result) - - if status == errSecSuccess, let data = result as? Data { - // Save to new service - if saveIdentityKey(data, forKey: "noiseStaticKey") { - migratedItems += 1 - SecureLogger.logKeyOperation("migrate", keyType: "noiseStaticKey", success: true) - } - // Delete from old service - let deleteQuery: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: oldService, - kSecAttrAccount as String: "identity_noiseStaticKey" - ] - SecItemDelete(deleteQuery as CFDictionary) - } - } - - // Clean up all other legacy items - for oldService in legacyServices { - let deleteQuery: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: oldService - ] - - SecItemDelete(deleteQuery as CFDictionary) - } - - - // Mark cleanup as done - UserDefaults.standard.set(true, forKey: cleanupKey) - } + private init() {} private func isSandboxed() -> Bool { @@ -240,11 +174,6 @@ class KeychainManager { return status == errSecSuccess || status == errSecItemNotFound } - // Force cleanup to run again (for development/testing) - func resetCleanupFlag() { - UserDefaults.standard.removeObject(forKey: "bitchat.keychain.cleanup.v2") - } - // Delete ALL keychain data for panic mode func deleteAllKeychainData() -> Bool { @@ -384,76 +313,4 @@ class KeychainManager { let key = "identity_noiseStaticKey" return retrieveData(forKey: key) != nil } - - // Aggressive cleanup for legacy items - can be called manually - func aggressiveCleanupLegacyItems() -> Int { - var deletedCount = 0 - - // List of KNOWN bitchat service names from our development history - let knownBitchatServices = [ - "com.bitchat.passwords", - "com.bitchat.deviceidentity", - "com.bitchat.noise.identity", - "chat.bitchat.passwords", - "bitchat.keychain", - "Bitchat", - "BitChat" - ] - - // First, delete all items from known legacy services - for legacyService in knownBitchatServices { - let deleteQuery: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: legacyService - ] - - let status = SecItemDelete(deleteQuery as CFDictionary) - if status == errSecSuccess { - deletedCount += 1 - } - } - - // Now search for items that have our specific account patterns with bitchat service names - let searchQuery: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecMatchLimit as String: kSecMatchLimitAll, - kSecReturnAttributes as String: true - ] - - var result: AnyObject? - let status = SecItemCopyMatching(searchQuery as CFDictionary, &result) - - if status == errSecSuccess, let items = result as? [[String: Any]] { - for item in items { - let account = item[kSecAttrAccount as String] as? String ?? "" - let service = item[kSecAttrService as String] as? String ?? "" - - // ONLY delete if service name contains "bitchat" somewhere - // This ensures we never touch other apps' keychain items - var shouldDelete = false - - // Check if service contains "bitchat" (case insensitive) but NOT our current service - let serviceLower = service.lowercased() - if service != self.service && serviceLower.contains("bitchat") { - shouldDelete = true - } - - if shouldDelete { - // Build precise delete query - let deleteQuery: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: service, - kSecAttrAccount as String: account - ] - - let deleteStatus = SecItemDelete(deleteQuery as CFDictionary) - if deleteStatus == errSecSuccess { - deletedCount += 1 - } - } - } - } - - return deletedCount - } }
\ No newline at end of file diff --git a/bitchat/Services/NoiseEncryptionService.swift b/bitchat/Services/NoiseEncryptionService.swift index 09c9fe7..5cd6ae3 100644 --- a/bitchat/Services/NoiseEncryptionService.swift +++ b/bitchat/Services/NoiseEncryptionService.swift @@ -419,24 +419,6 @@ class NoiseEncryptionService { SecureLogger.logSecurityEvent(.sessionExpired(peerID: peerID)) } - /// Migrate session when peer ID changes - func migratePeerSession(from oldPeerID: String, to newPeerID: String, fingerprint: String) { - // First update the fingerprint mappings - serviceQueue.sync(flags: .barrier) { - // Remove old mapping - if let oldFingerprint = peerFingerprints[oldPeerID], oldFingerprint == fingerprint { - peerFingerprints.removeValue(forKey: oldPeerID) - } - - // Add new mapping - peerFingerprints[newPeerID] = fingerprint - fingerprintToPeerID[fingerprint] = newPeerID - } - - // Migrate the session in session manager - sessionManager.migrateSession(from: oldPeerID, to: newPeerID) - } - // MARK: - Private Helpers private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) { diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index b8312a2..0a216dc 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -737,13 +737,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate { // Check if the recipient is blocked if isPeerBlocked(peerID) { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot send message to \(recipientNickname): user is blocked.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("cannot send message to \(recipientNickname): user is blocked.") return } @@ -803,13 +797,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate { category: SecureLogger.session, level: .warning) // Add system message to inform user - let systemMessage = BitchatMessage( - sender: "system", - content: "Cannot send message to \(recipientNickname) - peer is not reachable via mesh or Nostr.", - timestamp: Date(), - isRelay: false - ) - addMessage(systemMessage) + addSystemMessage("Cannot send message to \(recipientNickname) - peer is not reachable via mesh or Nostr.") } } @@ -864,27 +852,15 @@ class ChatViewModel: ObservableObject, BitchatDelegate { // Check if the peer is blocked if isPeerBlocked(peerID) { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot start chat with \(peerNickname): user is blocked.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("cannot start chat with \(peerNickname): user is blocked.") return } // Check if this is a moon peer (we favorite them but they don't favorite us) AND they're offline // Only require mutual favorites for offline Nostr messaging if let peer = peerIndex[peerID], - peer.isFavorite && !peer.theyFavoritedUs && !peer.isConnected && !peer.isRelayConnected { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot start chat with \(peerNickname): mutual favorite required for offline messaging.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + peer.isFavorite && !peer.theyFavoritedUs && !peer.isConnected { + addSystemMessage("cannot start chat with \(peerNickname): mutual favorite required for offline messaging.") return } @@ -1035,10 +1011,8 @@ class ChatViewModel: ObservableObject, BitchatDelegate { } @objc private func handleDeliveryAcknowledgment(_ notification: Notification) { - guard let messageId = notification.userInfo?["messageId"] as? String, - let senderNoiseKey = notification.userInfo?["senderNoiseKey"] as? Data else { return } + guard let messageId = notification.userInfo?["messageId"] as? String else { return } - let _ = senderNoiseKey.hexEncodedString() // Update the delivery status for the message @@ -1500,10 +1474,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate { privateChats.removeAll() unreadPrivateMessages.removeAll() - // First run aggressive cleanup to get rid of all legacy items - _ = KeychainManager.shared.aggressiveCleanupLegacyItems() - - // Then delete all current keychain data + // Delete all keychain data _ = KeychainManager.shared.deleteAllKeychainData() // Clear UserDefaults identity fallbacks @@ -2323,54 +2294,24 @@ class ChatViewModel: ObservableObject, BitchatDelegate { let messageContent = parts[2...].joined(separator: " ") sendPrivateMessage(messageContent, to: peerID) } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "started private chat with \(nickname)", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("started private chat with \(nickname)") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "user '\(nickname)' not found. they may be offline or using a different nickname.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("user '\(nickname)' not found. they may be offline or using a different nickname.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "usage: /m @nickname [message] or /m nickname [message]", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("usage: /m @nickname [message] or /m nickname [message]") } case "/w": let peerNicknames = meshService.getPeerNicknames() if connectedPeers.isEmpty { - let systemMessage = BitchatMessage( - sender: "system", - content: "no one else is online right now.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("no one else is online right now.") } else { let onlineList = connectedPeers.compactMap { peerID in peerNicknames[peerID] }.sorted().joined(separator: ", ") - let systemMessage = BitchatMessage( - sender: "system", - content: "online users: \(onlineList)", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("online users: \(onlineList)") } case "/clear": // Clear messages based on current context @@ -2493,13 +2434,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate { if let fingerprintStr = meshService.getPeerFingerprint(peerID) { if SecureIdentityStateManager.shared.isBlocked(fingerprint: fingerprintStr) { - let systemMessage = BitchatMessage( - sender: "system", - content: "\(nickname) is already blocked.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("\(nickname) is already blocked.") } else { // Update or create social identity with blocked status if var identity = SecureIdentityStateManager.shared.getSocialIdentity(for: fingerprintStr) { @@ -2523,42 +2458,18 @@ class ChatViewModel: ObservableObject, BitchatDelegate { blockedUsers.insert(fingerprintStr) favoritePeers.remove(fingerprintStr) - let systemMessage = BitchatMessage( - sender: "system", - content: "blocked \(nickname). you will no longer receive messages from them.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("blocked \(nickname). you will no longer receive messages from them.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot block \(nickname): unable to verify identity.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("cannot block \(nickname): unable to verify identity.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot block \(nickname): user not found.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("cannot block \(nickname): user not found.") } } else { // List blocked users if blockedUsers.isEmpty { - let systemMessage = BitchatMessage( - sender: "system", - content: "no blocked peers.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("no blocked peers.") } else { // Find nicknames for blocked users var blockedNicknames: [String] = [] @@ -2573,13 +2484,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate { } let blockedList = blockedNicknames.isEmpty ? "blocked peers (not currently online)" : blockedNicknames.sorted().joined(separator: ", ") - let systemMessage = BitchatMessage( - sender: "system", - content: "blocked peers: \(blockedList)", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("blocked peers: \(blockedList)") } } @@ -2601,48 +2506,18 @@ class ChatViewModel: ObservableObject, BitchatDelegate { // Update local set for UI blockedUsers.remove(fingerprintStr) - let systemMessage = BitchatMessage( - sender: "system", - content: "unblocked \(nickname).", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("unblocked \(nickname).") } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "\(nickname) is not blocked.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("\(nickname) is not blocked.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot unblock \(nickname): unable to verify identity.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("cannot unblock \(nickname): unable to verify identity.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "cannot unblock \(nickname): user not found.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("cannot unblock \(nickname): user not found.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "usage: /unblock <nickname>", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("usage: /unblock <nickname>") } case "/fav": @@ -2671,31 +2546,13 @@ class ChatViewModel: ObservableObject, BitchatDelegate { try? await self?.messageRouter?.sendFavoriteNotification(to: noisePublicKey, isFavorite: true) } - let systemMessage = BitchatMessage( - sender: "system", - content: "added \(nickname) to favorites.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("added \(nickname) to favorites.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "can't find peer: \(nickname)", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("can't find peer: \(nickname)") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "usage: /fav <nickname>", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("usage: /fav <nickname>") } case "/unfav": @@ -2718,41 +2575,17 @@ class ChatViewModel: ObservableObject, BitchatDelegate { try? await self?.messageRouter?.sendFavoriteNotification(to: noisePublicKey, isFavorite: false) } - let systemMessage = BitchatMessage( - sender: "system", - content: "removed \(nickname) from favorites.", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("removed \(nickname) from favorites.") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "can't find peer: \(nickname)", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("can't find peer: \(nickname)") } } else { - let systemMessage = BitchatMessage( - sender: "system", - content: "usage: /unfav <nickname>", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("usage: /unfav <nickname>") } case "/testnostr": - let systemMessage = BitchatMessage( - sender: "system", - content: "testing nostr relay connectivity...", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("testing nostr relay connectivity...") Task { @MainActor in if let relayManager = self.nostrRelayManager { @@ -2788,13 +2621,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate { default: // Unknown command - let systemMessage = BitchatMessage( - sender: "system", - content: "unknown command: \(cmd).", - timestamp: Date(), - isRelay: false - ) - messages.append(systemMessage) + addSystemMessage("unknown command: \(cmd).") } } @@ -3367,4 +3194,15 @@ class ChatViewModel: ObservableObject, BitchatDelegate { } + // MARK: - Helper for System Messages + private func addSystemMessage(_ content: String, timestamp: Date = Date()) { + let systemMessage = BitchatMessage( + sender: "system", + content: content, + timestamp: timestamp, + isRelay: false + ) + messages.append(systemMessage) + } + } // End of ChatViewModel class diff --git a/bitchat/Views/ContentView.swift b/bitchat/Views/ContentView.swift index f70090f..abe3309 100644 --- a/bitchat/Views/ContentView.swift +++ b/bitchat/Views/ContentView.swift @@ -271,7 +271,6 @@ struct ContentView: View { ForEach(windowedMessages, id: \.id) { message in VStack(alignment: .leading, spacing: 0) { // Check if current user is mentioned - let _ = message.mentions?.contains(viewModel.nickname) ?? false if message.sender == "system" { // System messages @@ -681,12 +680,6 @@ struct ContentView: View { .font(.system(size: 10)) .foregroundColor(textColor) .accessibilityLabel("Connected via mesh") - case .relayConnected: - // Chain link for relay connection - Image(systemName: "link") - .font(.system(size: 10)) - .foregroundColor(Color.blue) - .accessibilityLabel("Connected via relay") case .nostrAvailable: // Purple globe for mutual favorites reachable via Nostr Image(systemName: "globe") @@ -902,7 +895,7 @@ struct ContentView: View { let peerCounts = viewModel.allPeers.reduce(into: (others: 0, mesh: 0)) { counts, peer in guard peer.id != viewModel.meshService.myPeerID else { return } - let isMeshConnected = peer.isConnected || peer.isRelayConnected + let isMeshConnected = peer.isConnected if isMeshConnected { counts.mesh += 1 counts.others += 1 @@ -994,12 +987,6 @@ struct ContentView: View { .font(.system(size: 14)) .foregroundColor(textColor) .accessibilityLabel("Connected via mesh") - case .relayConnected: - // Chain link for relay connection - Image(systemName: "link") - .font(.system(size: 14)) - .foregroundColor(Color.blue) - .accessibilityLabel("Connected via relay") case .nostrAvailable: // Purple globe for Nostr Image(systemName: "globe") diff --git a/bitchatShareExtension/ShareViewController.swift b/bitchatShareExtension/ShareViewController.swift index 451f82c..65668cd 100644 --- a/bitchatShareExtension/ShareViewController.swift +++ b/bitchatShareExtension/ShareViewController.swift @@ -138,44 +138,6 @@ class ShareViewController: SLComposeServiceViewController { // MARK: - Helper Methods - private func handleSharedText(_ text: String) { - // Save to shared user defaults to pass to main app - saveToSharedDefaults(content: text, type: "text") - openMainApp() - } - - private func handleSharedURL(_ url: URL) { - // Get the page title if available from the extension context - var pageTitle: String? = nil - if let item = extensionContext?.inputItems.first as? NSExtensionItem { - pageTitle = item.attributedContentText?.string ?? item.attributedTitle?.string - } - - // Create a structured format for URL sharing - let urlData: [String: String] = [ - "url": url.absoluteString, - "title": pageTitle ?? url.host ?? "Shared Link" - ] - - // Convert to JSON string - if let jsonData = try? JSONSerialization.data(withJSONObject: urlData), - let jsonString = String(data: jsonData, encoding: .utf8) { - saveToSharedDefaults(content: jsonString, type: "url") - } else { - // Fallback to simple URL - saveToSharedDefaults(content: url.absoluteString, type: "url") - } - - openMainApp() - } - - private func handleSharedImage(_ image: UIImage) { - // For now, we'll just notify that image sharing isn't supported - // In the future, we could implement image sharing via the mesh - saveToSharedDefaults(content: "Image sharing coming soon!", type: "image") - openMainApp() - } - private func saveToSharedDefaults(content: String, type: String) { // Use app groups to share data between extension and main app guard let userDefaults = UserDefaults(suiteName: "group.chat.bitchat") else { diff --git a/bitchatTests/Mocks/MockNoiseSession.swift b/bitchatTests/Mocks/MockNoiseSession.swift deleted file mode 100644 index 1dc2811..0000000 --- a/bitchatTests/Mocks/MockNoiseSession.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// MockNoiseSession.swift -// bitchatTests -// -// This is free and unencumbered software released into the public domain. -// For more information, see <https://unlicense.org> -// - -import Foundation -import CryptoKit -@testable import bitchat - -class MockNoiseSession: NoiseSession { - var mockState: NoiseSessionState = .uninitialized - var shouldFailHandshake = false - var shouldFailEncryption = false - var handshakeMessages: [Data] = [] - var encryptedData: [Data] = [] - var decryptedData: [Data] = [] - - override func getState() -> NoiseSessionState { - return mockState - } - - override func isEstablished() -> Bool { - return mockState == .established - } - - override func startHandshake() throws -> Data { - if shouldFailHandshake { - mockState = .failed(NoiseSessionError.handshakeFailed(TestError.testFailure("Mock handshake failure"))) - throw NoiseSessionError.handshakeFailed(TestError.testFailure("Mock handshake failure")) - } - - mockState = .handshaking - let handshakeData = TestHelpers.generateRandomData(length: 32) - handshakeMessages.append(handshakeData) - return handshakeData - } - - override func processHandshakeMessage(_ message: Data) throws -> Data? { - if shouldFailHandshake { - mockState = .failed(NoiseSessionError.handshakeFailed(TestError.testFailure("Mock handshake failure"))) - throw NoiseSessionError.handshakeFailed(TestError.testFailure("Mock handshake failure")) - } - - handshakeMessages.append(message) - - // Simulate handshake completion after 2 messages - if handshakeMessages.count >= 2 { - mockState = .established - return nil - } else { - let response = TestHelpers.generateRandomData(length: 48) - handshakeMessages.append(response) - return response - } - } - - override func encrypt(_ plaintext: Data) throws -> Data { - if shouldFailEncryption { - throw NoiseSessionError.notEstablished - } - - guard mockState == .established else { - throw NoiseSessionError.notEstablished - } - - // Simple mock encryption: prepend magic bytes and append the data - var encrypted = Data([0xDE, 0xAD, 0xBE, 0xEF]) - encrypted.append(plaintext) - encryptedData.append(encrypted) - return encrypted - } - - override func decrypt(_ ciphertext: Data) throws -> Data { - if shouldFailEncryption { - throw NoiseSessionError.notEstablished - } - - guard mockState == .established else { - throw NoiseSessionError.notEstablished - } - - // Simple mock decryption: remove magic bytes - guard ciphertext.count > 4 else { - throw TestError.testFailure("Invalid ciphertext") - } - - let plaintext = ciphertext.dropFirst(4) - decryptedData.append(plaintext) - return plaintext - } - - override func reset() { - mockState = .uninitialized - handshakeMessages.removeAll() - encryptedData.removeAll() - decryptedData.removeAll() - } -}
\ No newline at end of file diff --git a/bitchatTests/Noise/NoiseProtocolTests.swift b/bitchatTests/Noise/NoiseProtocolTests.swift index f54275d..be82ac7 100644 --- a/bitchatTests/Noise/NoiseProtocolTests.swift +++ b/bitchatTests/Noise/NoiseProtocolTests.swift @@ -226,23 +226,6 @@ final class NoiseProtocolTests: XCTestCase { XCTAssertEqual(decrypted, plaintext) } - func testSessionMigration() throws { - let manager = NoiseSessionManager(localStaticKey: aliceKey) - - // Create and establish a session - _ = try manager.initiateHandshake(with: TestConstants.testPeerID2) - - // Migrate to new peer ID - let newPeerID = TestConstants.testPeerID3 - manager.migrateSession(from: TestConstants.testPeerID2, to: newPeerID) - - // Old peer ID should not have session - XCTAssertNil(manager.getSession(for: TestConstants.testPeerID2)) - - // New peer ID should have the session - XCTAssertNotNil(manager.getSession(for: newPeerID)) - } - // MARK: - Security Tests func testTamperedCiphertextDetection() throws { |
