Server information (Monal-style): - Add ServerInformation model and ServerInfoView reachable from Settings, showing server software (XEP-0092), disco#info identities and features, conference (MUC) services via disco#items, and STUN/TURN via XEP-0215. - Add a curated XEP capability list with per-XEP success/error status derived from server/account disco features and connection flags. - Detect PEP (0163) from account disco pubsub#* features, HTTP Upload (0363) from the main domain plus its components, and add Roster Versioning (0237), Pre-Authenticated Roster Subscription (0379), and SASL SCRAM Downgrade Protection (0474, neutral because Martin does not expose SSDP). Connection statistics: - Add LumaConnectionStatsModule, an XmppStanzaFilter registered before stream management, to count sent / acknowledged / received stanzas. - Track last login and SMACKS session timestamps, and read SASL mechanisms and Client State Indication from the raw stream features. MAM / OMEMO handling: - Prefer a plaintext <body> fallback when OMEMO decryption fails instead of showing "failed to decrypt". - Keep the optimistic text of our own outgoing message when its echo cannot be decrypted back, instead of replacing it with a placeholder. Emoji picker: - Add EmojiCatalog and a cross-platform EmojiPickerView. - Open the picker for reactions (replacing the static quick list) and from the composer's smiley button, inserting the chosen emoji into the draft.
157 lines
6.6 KiB
Swift
157 lines
6.6 KiB
Swift
import Foundation
|
|
|
|
/// Snapshot of everything the "server details" screen shows, modelled after
|
|
/// Monal's `ServerDetails`: server software (XEP-0092), disco#info identities
|
|
/// and features (XEP-0030), conference (MUC) services, STUN/TURN services
|
|
/// (XEP-0215) and a few connection-level capability flags.
|
|
struct ServerInformation: Equatable, Sendable {
|
|
enum CapabilityStatus: Equatable, Sendable {
|
|
case success, normal, warning, error
|
|
}
|
|
|
|
struct Software: Equatable, Sendable {
|
|
let name: String?
|
|
let version: String?
|
|
let os: String?
|
|
}
|
|
|
|
struct Identity: Equatable, Sendable, Identifiable {
|
|
let category: String
|
|
let type: String
|
|
let name: String?
|
|
|
|
var id: String { "\(category)/\(type)" }
|
|
|
|
var title: String { "\(category) / \(type)" }
|
|
}
|
|
|
|
struct ConferenceServer: Equatable, Sendable, Identifiable {
|
|
let jid: String
|
|
let name: String?
|
|
let type: String
|
|
let category: String
|
|
|
|
var id: String { jid }
|
|
}
|
|
|
|
struct ExternalService: Equatable, Sendable, Identifiable {
|
|
let type: String
|
|
let host: String
|
|
let port: UInt16?
|
|
let transport: String?
|
|
|
|
var id: String { "\(type)|\(host)|\(port.map(String.init) ?? "")" }
|
|
}
|
|
|
|
struct ConnectionStats: Equatable, Sendable {
|
|
let lastLogin: Date?
|
|
let smacksSessionEstablished: Date?
|
|
let sent: UInt32
|
|
let acknowledged: UInt32
|
|
let received: UInt32
|
|
|
|
var unacknowledged: UInt32 {
|
|
sent > acknowledged ? sent - acknowledged : 0
|
|
}
|
|
}
|
|
|
|
struct Capability: Equatable, Sendable, Identifiable {
|
|
let title: String
|
|
let detail: String
|
|
let status: CapabilityStatus
|
|
|
|
var id: String { title }
|
|
}
|
|
|
|
let serverDomain: String
|
|
let software: Software
|
|
let identities: [Identity]
|
|
let serverFeatures: [String]
|
|
let accountFeatures: [String]
|
|
let conferenceServers: [ConferenceServer]
|
|
let externalServices: [ExternalService]
|
|
let connectionStats: ConnectionStats
|
|
let saslMethods: [String]
|
|
let supportsStreamManagement: Bool
|
|
let supportsCarbons: Bool
|
|
let supportsClientState: Bool
|
|
let supportsHTTPUpload: Bool
|
|
let supportsRosterVersioning: Bool
|
|
let supportsRosterPreApproval: Bool
|
|
|
|
/// The curated XEP list Monal shows, with a status derived from the server
|
|
/// and account disco features plus the connection-level flags.
|
|
var capabilities: [Capability] {
|
|
let server = Set(serverFeatures)
|
|
let account = Set(accountFeatures)
|
|
let supportsPubSub = account.contains { $0.hasPrefix("http://jabber.org/protocol/pubsub") }
|
|
return [
|
|
Capability(
|
|
title: "XEP-0163 Personal Eventing Protocol",
|
|
detail: "Публикация событий состояния аккаунта (аватары, статусы) через publish-subscribe.",
|
|
status: supportsPubSub ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0191 Blocking Command",
|
|
detail: "Расширение XMPP для блокировки контактов.",
|
|
status: server.contains("urn:xmpp:blocking") ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0198 Stream Management",
|
|
detail: "Возобновление потока при обрыве соединения — быстрее переподключение и экономия заряда.",
|
|
status: supportsStreamManagement ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0199 XMPP Ping",
|
|
detail: "Пинг уровня приложения поверх XML-потока.",
|
|
status: server.contains("urn:xmpp:ping") ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0215 External Service Discovery",
|
|
detail: "Обнаружение внешних сервисов (STUN/TURN) для аудио- и видеозвонков.",
|
|
status: server.contains("urn:xmpp:extdisco:2") ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0237 Roster Versioning",
|
|
detail: "Версионирование ростера, чтобы сервер не отправлял его повторно, если он не менялся.",
|
|
status: supportsRosterVersioning ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0280 Message Carbons",
|
|
detail: "Синхронизация сообщений между всеми вашими устройствами.",
|
|
status: supportsCarbons ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0313 Message Archive Management",
|
|
detail: "Доступ к архиву сообщений на сервере.",
|
|
status: account.contains("urn:xmpp:mam:2") ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0352 Client State Indication",
|
|
detail: "Указывает, активно ли устройство. Экономит заряд.",
|
|
status: supportsClientState ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0357 Push Notifications",
|
|
detail: "Push-уведомления через Apple даже когда приложение выгружено.",
|
|
status: account.contains("urn:xmpp:push:0") ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0363 HTTP File Upload",
|
|
detail: "Загрузка файлов на сервер для обмена с собеседниками.",
|
|
status: supportsHTTPUpload ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0379 Pre-Authenticated Roster Subscription",
|
|
detail: "Ссылки для предварительно аутентифицированной подписки на присутствие.",
|
|
status: supportsRosterPreApproval ? .success : .error
|
|
),
|
|
Capability(
|
|
title: "XEP-0474 SASL SCRAM Downgrade Protection",
|
|
detail: "Защита SASL/SASL2-рукопожатия от понижения метода и channel-binding. Определяется во время SCRAM-аутентификации, а не рекламируется статически.",
|
|
status: .normal
|
|
),
|
|
]
|
|
}
|
|
}
|