wip
This commit is contained in:
parent
ced4175906
commit
0dff34318b
|
@ -31,14 +31,14 @@ struct TestScreen: View {
|
||||||
.background { Color.blue.opacity(0.4) }
|
.background { Color.blue.opacity(0.4) }
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
cls.addContact(jidStr: "asdadad33@asdfsdf.df", name: "sdsd")
|
cls.addContact(jidStr: "deltest@asdfsdf.df", name: "sdsd")
|
||||||
} label: {
|
} label: {
|
||||||
Text("Add contact")
|
Text("Add contact")
|
||||||
.padding()
|
.padding()
|
||||||
.background { Color.blue.opacity(0.4) }
|
.background { Color.blue.opacity(0.4) }
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
cls.deleteContact(jidStr: "asdadad11@asdfsdf.df")
|
cls.deleteContact(jidStr: "deltest@asdfsdf.df")
|
||||||
} label: {
|
} label: {
|
||||||
Text("Remove contact")
|
Text("Remove contact")
|
||||||
.padding()
|
.padding()
|
||||||
|
|
|
@ -91,6 +91,14 @@ extension Stanza {
|
||||||
buildIq(direction: "set", from: from, to: nil, payload: payload)
|
buildIq(direction: "set", from: from, to: nil, payload: payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func iqResult(payload: XMLElement) -> Stanza? {
|
||||||
|
buildIq(direction: "result", from: nil, to: nil, payload: payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func iqResult(from: String, payload: XMLElement) -> Stanza? {
|
||||||
|
buildIq(direction: "result", from: from, to: nil, payload: payload)
|
||||||
|
}
|
||||||
|
|
||||||
// build iq stanza
|
// build iq stanza
|
||||||
private static func buildIq(direction: String, from: String?, to: String?, payload: XMLElement) -> Stanza? {
|
private static func buildIq(direction: String, from: String?, to: String?, payload: XMLElement) -> Stanza? {
|
||||||
var attributes = ["id": XMLElement.randomId, "type": direction]
|
var attributes = ["id": XMLElement.randomId, "type": direction]
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// XEP-0237
|
// XEP-0237
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
// TODO: implement error catching
|
|
||||||
final class RosterModule: XmppModule {
|
final class RosterModule: XmppModule {
|
||||||
let id = "Roseter module"
|
let id = "Roseter module"
|
||||||
|
|
||||||
|
@ -123,6 +122,14 @@ final class RosterModule: XmppModule {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we signaling "result" to server it means roster was updated on this resource
|
||||||
|
case .stanzaOutbound(let stanza):
|
||||||
|
if let query = stanza.wrapped.nodes.first(where: { $0.name == "query" }), query.xmlns == "jabber:iq:roster", stanza.type == .iq(.result) {
|
||||||
|
return .rosterUpdated
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -134,7 +141,7 @@ private extension RosterModule {
|
||||||
switch stanza.type {
|
switch stanza.type {
|
||||||
// "set" type stanza from server is just "push", so change roster accordingly
|
// "set" type stanza from server is just "push", so change roster accordingly
|
||||||
case .iq(.set):
|
case .iq(.set):
|
||||||
// sanity check
|
// sanity check (according RFC6121 skip this push if its not for current resource)
|
||||||
if stanza.wrapped.attributes["to"] != state.jid.bare {
|
if stanza.wrapped.attributes["to"] != state.jid.bare {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -164,9 +171,25 @@ private extension RosterModule {
|
||||||
// save roster
|
// save roster
|
||||||
guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil }
|
guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil }
|
||||||
await storage?.setRoster(jid: state.jid, roster: data)
|
await storage?.setRoster(jid: state.jid, roster: data)
|
||||||
return .rosterUpdated
|
|
||||||
|
|
||||||
// the "result" is an answer from one of our "set", or initial roster request
|
// according RFC6120 it is not necessary to answer server with "result" on push, but why not?
|
||||||
|
let result = Stanza.iqResult(
|
||||||
|
from: state.jid.full,
|
||||||
|
payload: XMLElement(
|
||||||
|
name: "query",
|
||||||
|
xmlns: "jabber:iq:roster",
|
||||||
|
attributes: [:],
|
||||||
|
content: nil,
|
||||||
|
nodes: []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if let result {
|
||||||
|
return .stanzaOutbound(result)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// the "result" from server is an answer from one of our "set", or initial roster request
|
||||||
case .iq(.result):
|
case .iq(.result):
|
||||||
// get request stanza from registry
|
// get request stanza from registry
|
||||||
guard let respId = stanza.id, let req = await registry.deuque(for: respId) else { return nil }
|
guard let respId = stanza.id, let req = await registry.deuque(for: respId) else { return nil }
|
||||||
|
@ -201,17 +224,35 @@ private extension RosterModule {
|
||||||
|
|
||||||
// for result of one of our request
|
// for result of one of our request
|
||||||
case .iq(.set):
|
case .iq(.set):
|
||||||
break
|
// get item from request stanza
|
||||||
|
let xmlItem = req.wrapped
|
||||||
|
.nodes
|
||||||
|
.first(where: { $0.name == "query" })?
|
||||||
|
.nodes
|
||||||
|
.first(where: { $0.name == "item" })
|
||||||
|
guard let xmlItem, let item = RosterItem(wrap: xmlItem, owner: state.jid) else { return nil }
|
||||||
|
|
||||||
|
// filter out exists items
|
||||||
|
existItems = existItems.filter { $0.id != item.id }
|
||||||
|
|
||||||
|
// append updated/new item (if it not "removed")
|
||||||
|
if item.subsription != .remove {
|
||||||
|
existItems.append(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save roster
|
||||||
|
guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil }
|
||||||
|
await storage?.setRoster(jid: state.jid, roster: data)
|
||||||
|
return .rosterUpdated
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// append items from requested roster
|
|
||||||
return nil
|
|
||||||
|
|
||||||
// TODO: add error handling here
|
// TODO: add error handling here
|
||||||
case .iq(.error):
|
case .iq(.error):
|
||||||
|
guard let respId = stanza.id, let req = await registry.deuque(for: respId) else { return nil }
|
||||||
|
print("Error on request: \(req.wrapped)\n with: \(stanza.wrapped)")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -219,89 +260,3 @@ private extension RosterModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private extension RosterModule {
|
|
||||||
// private func update(state: ClientState, jidStr: String, args: [String: String]) async -> Event? {
|
|
||||||
// print(state, jidStr, args)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func delete(state: ClientState, jidStr: String) async -> Event? {
|
|
||||||
// var existItems: [RosterItem] = []
|
|
||||||
// guard
|
|
||||||
// let data = await storage?.getRoster(jid: state.jid),
|
|
||||||
// let decoded = try? JSONDecoder().decode([XMLElement].self, from: data),
|
|
||||||
// let jid = try? JID(jidStr)
|
|
||||||
// else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// existItems = decoded.compactMap { RosterItem(wrap: $0, owner: state.jid) }
|
|
||||||
// existItems = existItems.filter { $0.jid != jid }
|
|
||||||
// return .rosterUpdated
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func processSet(state: ClientState, stanza: Stanza) async -> Event? {
|
|
||||||
// // sanity check
|
|
||||||
// if stanza.wrapped.attributes["from"] != state.jid.bare {
|
|
||||||
// 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
|
|
||||||
// let items = stanza.wrapped
|
|
||||||
// .nodes
|
|
||||||
// .first(where: { $0.name == "query" })?
|
|
||||||
// .nodes
|
|
||||||
// .filter { $0.name == "item" } ?? []
|
|
||||||
// for item in items {
|
|
||||||
// guard let itemJidStr = item.attributes["jid"], let itemJid = try? JID(itemJidStr) else { continue }
|
|
||||||
// let subscription = item.attributes["subscription"]
|
|
||||||
// switch subscription {
|
|
||||||
// // TODO: scheck subscription type for removed contacts
|
|
||||||
// // on different servers
|
|
||||||
// case "remove":
|
|
||||||
// existItems = existItems.filter { $0.jid == itemJid }
|
|
||||||
//
|
|
||||||
// // by default just update roster (or add it if its new)
|
|
||||||
// default:
|
|
||||||
// if let rosterItem = RosterItem(wrap: item, owner: state.jid) {
|
|
||||||
// existItems = existItems.filter { $0.jid == itemJid }
|
|
||||||
// existItems.append(rosterItem)
|
|
||||||
// } else {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // save roster
|
|
||||||
// guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil }
|
|
||||||
// await storage?.setRoster(jid: state.jid, roster: data)
|
|
||||||
//
|
|
||||||
// // 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) async -> Event? {
|
|
||||||
// print("--WE HERE 2!")
|
|
||||||
// print(stanza)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
Loading…
Reference in a new issue