This commit is contained in:
fmodf 2024-12-16 13:51:12 +01:00
parent ff6f3f3ee2
commit ab01430aae
5 changed files with 54 additions and 7 deletions

View file

@ -38,7 +38,12 @@ enum Event {
case bindStream case bindStream
case bindStreamDone(String) case bindStreamDone(String)
case bindStreamError case bindStreamError
// stream established, RFC-6120 procedure done
case streamReady case streamReady
case requestRoster
// case gotRoster // by request or by server push
} }
// MARK: State // MARK: State
@ -83,6 +88,7 @@ struct ClientState: Codable & Equatable {
} }
} }
// MARK: Client
final class XMPPClient { final class XMPPClient {
private var state = ClientState.initial private var state = ClientState.initial
private let logger = ClientLogger() private let logger = ClientLogger()
@ -94,7 +100,8 @@ final class XMPPClient {
SessionModule(), SessionModule(),
AuthorizationModule(self.storage), AuthorizationModule(self.storage),
StanzaModule(self.storage), StanzaModule(self.storage),
DiscoveryModule() DiscoveryModule(),
RosterModule()
] ]
init(storage: any XMPPClientStorageProtocol, userAgent: UserAgent) { init(storage: any XMPPClientStorageProtocol, userAgent: UserAgent) {
@ -103,7 +110,7 @@ final class XMPPClient {
} }
func tryLogin(jid: JID, credentialsId: UUID) { func tryLogin(jid: JID, credentialsId: UUID) {
logger.update(jid.description) logger.update(jid.full)
Task { Task {
await fire(.startClientLogin(jid: jid, credsId: credentialsId)) await fire(.startClientLogin(jid: jid, credsId: credentialsId))
} }

View file

@ -10,7 +10,7 @@ struct JID: Hashable, CustomStringConvertible, Codable, Equatable {
resourcePart = parts.2 resourcePart = parts.2
} }
var description: String { var full: String {
var str = "\(localPart)@\(domainPart)" var str = "\(localPart)@\(domainPart)"
if let resource = resourcePart { if let resource = resourcePart {
str += "/\(resource)" str += "/\(resource)"
@ -18,6 +18,10 @@ struct JID: Hashable, CustomStringConvertible, Codable, Equatable {
return str return str
} }
var description: String {
full
}
var hash: Int { var hash: Int {
if let resourcePart { if let resourcePart {
return "\(localPart)@\(domainPart)/\(resourcePart)".hashValue return "\(localPart)@\(domainPart)/\(resourcePart)".hashValue

View file

@ -75,18 +75,26 @@ struct Stanza {
// MARK: Init // MARK: Init
extension Stanza { extension Stanza {
static func iqGet(payload: XMLElement) -> Stanza? { static func iqGet(jid: String? = nil, payload: XMLElement) -> Stanza? {
var attributes = ["id": XMLElement.randomId, "type": "get"]
if let jid {
attributes["from"] = jid
}
let req = XMLElement( let req = XMLElement(
name: "iq", name: "iq",
xmlns: nil, xmlns: nil,
attributes: ["type": "get", "id": XMLElement.randomId], attributes: attributes,
content: nil, content: nil,
nodes: [payload] nodes: [payload]
) )
return Stanza(wrap: req) return Stanza(wrap: req)
} }
static func iqSet(payload: XMLElement) -> Stanza? { static func iqSet(jid: String? = nil, payload: XMLElement) -> Stanza? {
var attributes = ["id": XMLElement.randomId, "type": "get"]
if let jid {
attributes["from"] = jid
}
let req = XMLElement( let req = XMLElement(
name: "iq", name: "iq",
xmlns: nil, xmlns: nil,

View file

@ -0,0 +1,28 @@
import Foundation
final class RosterModule: XmppModule {
let id = "Roseter module"
func reduce(oldState: ClientState, with _: Event) -> ClientState {
oldState
}
func process(state: ClientState, with event: Event) async -> Event? {
switch event {
case .streamReady:
return .requestRoster
case .requestRoster:
// TODO: check version!
let req = Stanza.iqGet(jid: state.jid.full, payload: XMLElement(name: "query", xmlns: "jabber:iq:roster", attributes: [:], content: nil, nodes: []))
if let req {
return .stanzaOutbound(req)
} else {
return nil
}
default:
return nil
}
}
}

View file

@ -92,7 +92,7 @@ final class SessionModule: XmppModule {
name: "stream:stream", name: "stream:stream",
xmlns: nil, xmlns: nil,
attributes: [ attributes: [
"from": state.jid.description, "from": state.jid.bare,
"to": state.jid.domainPart, "to": state.jid.domainPart,
"xml:lang": "en", "xml:lang": "en",
"version": "1.0", "version": "1.0",