mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 23:25:20 +00:00
Fix connection tracking and timestamp compatibility issues
- Fix 'alone :/' bug by not requiring nicknames for peer counting - Handle both second and millisecond timestamps for compatibility with older clients - Fix NaN errors in autocomplete positioning with bounds checking - Simplify getAllConnectedPeerIDs to show all valid active peers - Add timestamp conversion logic to handle ~55 year time difference bug
This commit is contained in:
@@ -756,29 +756,16 @@ class BluetoothMeshService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func getAllConnectedPeerIDs() -> [String] {
|
private func getAllConnectedPeerIDs() -> [String] {
|
||||||
// Only return peers who have announced (have nicknames)
|
// Return all valid active peers
|
||||||
let announcedPeers = Set(activePeers.compactMap { peerID -> String? in
|
let validPeers = activePeers.filter { peerID in
|
||||||
// Ensure peerID is valid and not nil
|
// Ensure peerID is valid
|
||||||
guard !peerID.isEmpty,
|
return !peerID.isEmpty &&
|
||||||
peerID != "unknown",
|
peerID != "unknown" &&
|
||||||
peerID != myPeerID,
|
peerID != myPeerID &&
|
||||||
peerID.count <= 8 else { // Filter out temp IDs
|
peerID.count <= 8 // Filter out temp IDs
|
||||||
return nil
|
}
|
||||||
}
|
|
||||||
|
return Array(validPeers).sorted()
|
||||||
// Safely check if peer has a nickname (thread-safe)
|
|
||||||
peerNicknamesLock.lock()
|
|
||||||
let hasNickname = peerNicknames[peerID] != nil
|
|
||||||
peerNicknamesLock.unlock()
|
|
||||||
|
|
||||||
if hasNickname {
|
|
||||||
return peerID
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
// Active peers: \(announcedPeers.count)
|
|
||||||
return Array(announcedPeers).sorted()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Store-and-Forward Methods
|
// MARK: - Store-and-Forward Methods
|
||||||
@@ -817,9 +804,19 @@ class BluetoothMeshService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create stored message with original packet timestamp preserved
|
// Create stored message with original packet timestamp preserved
|
||||||
|
// Handle both seconds and milliseconds timestamps
|
||||||
|
let timestampInSeconds: TimeInterval
|
||||||
|
if packet.timestamp > 1_000_000_000_000 {
|
||||||
|
// Already in milliseconds
|
||||||
|
timestampInSeconds = TimeInterval(packet.timestamp) / 1000.0
|
||||||
|
} else {
|
||||||
|
// Already in seconds
|
||||||
|
timestampInSeconds = TimeInterval(packet.timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
let storedMessage = StoredMessage(
|
let storedMessage = StoredMessage(
|
||||||
packet: packet,
|
packet: packet,
|
||||||
timestamp: Date(timeIntervalSince1970: TimeInterval(packet.timestamp) / 1000.0), // convert from milliseconds
|
timestamp: Date(timeIntervalSince1970: timestampInSeconds),
|
||||||
messageID: messageID,
|
messageID: messageID,
|
||||||
isForFavorite: isForFavorite
|
isForFavorite: isForFavorite
|
||||||
)
|
)
|
||||||
@@ -1056,7 +1053,18 @@ class BluetoothMeshService: NSObject {
|
|||||||
|
|
||||||
// Replay attack protection: Check timestamp is within reasonable window (5 minutes)
|
// Replay attack protection: Check timestamp is within reasonable window (5 minutes)
|
||||||
let currentTime = UInt64(Date().timeIntervalSince1970 * 1000) // milliseconds
|
let currentTime = UInt64(Date().timeIntervalSince1970 * 1000) // milliseconds
|
||||||
let timeDiff = abs(Int64(currentTime) - Int64(packet.timestamp))
|
|
||||||
|
// Handle both seconds and milliseconds timestamps for compatibility
|
||||||
|
let packetTime: UInt64
|
||||||
|
if packet.timestamp > 1_000_000_000_000 {
|
||||||
|
// Already in milliseconds
|
||||||
|
packetTime = packet.timestamp
|
||||||
|
} else {
|
||||||
|
// Convert seconds to milliseconds
|
||||||
|
packetTime = packet.timestamp * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeDiff = abs(Int64(currentTime) - Int64(packetTime))
|
||||||
if timeDiff > 300000 { // 5 minutes in milliseconds
|
if timeDiff > 300000 { // 5 minutes in milliseconds
|
||||||
print("[SECURITY] Dropping packet with timestamp too far from current time: \(timeDiff/1000) seconds")
|
print("[SECURITY] Dropping packet with timestamp too far from current time: \(timeDiff/1000) seconds")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -106,8 +106,11 @@ struct ContentView: View {
|
|||||||
// Calculate approximate position based on nickname length and @ position
|
// Calculate approximate position based on nickname length and @ position
|
||||||
let nicknameWidth: CGFloat = viewModel.selectedPrivateChatPeer != nil ? 90 : 80
|
let nicknameWidth: CGFloat = viewModel.selectedPrivateChatPeer != nil ? 90 : 80
|
||||||
let charWidth: CGFloat = 8.5 // Approximate width of monospace character
|
let charWidth: CGFloat = 8.5 // Approximate width of monospace character
|
||||||
let atPosition = viewModel.autocompleteRange?.location ?? 0
|
let atPosition = CGFloat(viewModel.autocompleteRange?.location ?? 0)
|
||||||
let offsetX = nicknameWidth + (CGFloat(atPosition) * charWidth)
|
let offsetX = nicknameWidth + (atPosition * charWidth)
|
||||||
|
|
||||||
|
// Ensure offsetX is valid (not NaN or infinite)
|
||||||
|
let safeOffsetX = offsetX.isFinite ? offsetX : nicknameWidth
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
ForEach(Array(viewModel.autocompleteSuggestions.enumerated()), id: \.element) { index, suggestion in
|
ForEach(Array(viewModel.autocompleteSuggestions.enumerated()), id: \.element) { index, suggestion in
|
||||||
@@ -133,7 +136,7 @@ struct ContentView: View {
|
|||||||
.stroke(secondaryTextColor.opacity(0.5), lineWidth: 1)
|
.stroke(secondaryTextColor.opacity(0.5), lineWidth: 1)
|
||||||
)
|
)
|
||||||
.frame(width: 150, alignment: .leading)
|
.frame(width: 150, alignment: .leading)
|
||||||
.offset(x: min(offsetX, geometry.size.width - 180)) // Prevent going off-screen
|
.offset(x: min(safeOffsetX, max(0, geometry.size.width - 180))) // Prevent going off-screen
|
||||||
.padding(.bottom, 45) // Position just above input
|
.padding(.bottom, 45) // Position just above input
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
Reference in New Issue
Block a user