another.im-ios/AnotherXMPP/modules/roster/RosterModule.swift

175 lines
6.2 KiB
Swift
Raw Normal View History

2024-12-16 12:51:12 +00:00
import Foundation
2024-12-17 10:00:51 +00:00
// TODO: add versioning (XEP-0237) if needed
2024-12-17 15:28:30 +00:00
// TODO: implement error catching
2024-12-16 12:51:12 +00:00
final class RosterModule: XmppModule {
let id = "Roseter module"
2024-12-17 10:00:51 +00:00
private weak var storage: (any XMPPStorage)?
2024-12-17 16:07:13 +00:00
private var fullReqId = ""
2024-12-17 10:00:51 +00:00
init(_ storage: any XMPPStorage) {
self.storage = storage
}
2024-12-16 12:51:12 +00:00
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:
2024-12-17 10:00:51 +00:00
let req = Stanza.iqGet(
from: state.jid.full,
payload: XMLElement(name: "query", xmlns: "jabber:iq:roster", attributes: [:], content: nil, nodes: [])
)
2024-12-16 12:51:12 +00:00
if let req {
2024-12-17 16:07:13 +00:00
fullReqId = req.id ?? "???"
2024-12-16 12:51:12 +00:00
return .stanzaOutbound(req)
} else {
return nil
}
2024-12-24 10:43:15 +00:00
case .addRosterItem(let jidStr, let args):
return update(jidStr: jidStr, args: args)
case .updateRosterItem(let jidStr, let args):
return update(jidStr: jidStr, args: args)
case .deleteRosterItem(let jidStr):
return delete(jidStr: jidStr)
2024-12-17 10:00:51 +00:00
case .stanzaInbound(let stanza):
2024-12-17 15:28:30 +00:00
if let query = stanza.wrapped.nodes.first(where: { $0.name == "query" }), query.xmlns == "jabber:iq:roster" {
2024-12-24 10:43:15 +00:00
if stanza.type == .iq(.set) {
return processSet(state: state, stanza: stanza)
} else if stanza.type == .iq(.result) {
return processResult(state: state, stanza: stanza)
} else {
return nil
}
2024-12-17 15:28:30 +00:00
} else {
return nil
2024-12-17 10:00:51 +00:00
}
2024-12-16 12:51:12 +00:00
default:
return nil
}
}
}
2024-12-17 10:00:51 +00:00
private extension RosterModule {
2024-12-24 10:43:15 +00:00
private func update(jidStr: String, args: [String: String]) -> Event? {
print(jidStr, args)
return nil
}
2024-12-17 10:00:51 +00:00
2024-12-24 10:43:15 +00:00
private func delete(jidStr: String) -> Event? {
print(jidStr)
return nil
2024-12-17 10:00:51 +00:00
}
2024-12-24 10:43:15 +00:00
private func processSet(state: ClientState, stanza: Stanza) -> Event? {
// sanity check
if stanza.wrapped.attributes["from"] != state.jid.bare {
2024-12-17 16:07:13 +00:00
return nil
}
2024-12-24 10:43:15 +00:00
// process
// according to RFC-6121 a set from server (push)
// shouyld be answered with result
guard
let res = Stanza(wrap: XMLElement(
name: "iq",
xmlns: nil,
attributes: [
"from": state.jid.full,
"id": stanza.id ?? "???",
"type": "result"
],
content: nil,
nodes: []
)) else { return nil }
return .stanzaOutbound(res)
}
private func processResult(state _: ClientState, stanza: Stanza) -> Event? {
print(stanza)
return nil
2024-12-17 16:07:13 +00:00
}
2024-12-24 10:43:15 +00:00
// <iq from=juliet@example.com/balcony
// id=a78b4q6ha463
// type=result/>
// private func processRoster(state: ClientState, stanza: Stanza) async -> Event? {
// // get inner query
// guard let query = stanza.wrapped.nodes.first(where: { $0.name == "query" })
// else { return nil }
//
// // get exists roster items
// var existItems: [RosterItem] = []
// if let data = await storage?.getRoster(jid: state.jid), let decoded = try? JSONDecoder().decode([XMLElement].self, from: data) {
// existItems = decoded.compactMap { RosterItem(wrap: $0, owner: state.jid) }
// }
//
// // process push (.set from server)
// if stanza.type == .iq(.set) {
// guard
// let item = query.nodes.first(where: { $0.name == "item" }),
// let new = RosterItem(wrap: item, owner: state.jid)
// else { return nil }
// existItems = existItems.filter { $0.jid != new.jid }
// existItems.append(new)
// guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil }
// await storage?.setRoster(jid: state.jid, roster: data)
// return .rosterUpdated
//
// // process .result from server
// } else if stanza.type == .iq(.result) {
// // process full list
// if stanza.id == fullReqId {
// let items = query.nodes.filter { $0.name == "item" }
// guard let data = try? JSONEncoder().encode(items) else { return nil }
// await storage?.setRoster(jid: state.jid, roster: data)
//
// // process changed item
// } else {
// guard
// let item = query.nodes.first(where: { $0.name == "item" }),
// let new = RosterItem(wrap: item, owner: state.jid)
// else { return nil }
// existItems = existItems.filter { $0.jid != new.jid }
// if new.subsription != .remove, new.subsription != .none {
// existItems.append(new)
// guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil }
// await storage?.setRoster(jid: state.jid, roster: data)
// } else {
// // do nothing, this item was removed
// }
// }
// return .rosterUpdated
// } else {
// return nil
// }
// }
// private func updRoster(state: ClientState, target: String, remove: Bool) -> Event? {
// var attributes = ["jid": target]
// if remove {
// attributes["subcription"] = "remove"
// }
// let item = XMLElement(name: "item", xmlns: "jabber:iq:roster", attributes: attributes, content: nil, nodes: [])
// let query = XMLElement(name: "query", xmlns: "jabber:iq:roster", attributes: [:], content: nil, nodes: [item])
// if let req = Stanza.iqSet(from: state.jid.full, payload: query) {
// return .stanzaOutbound(req)
// } else {
// return nil
// }
// }
2024-12-17 16:07:13 +00:00
}