Compare commits
No commits in common. "c844e7932e91c0b6f4006ef6ed777b427b122b1c" and "aa7a5e89ec043c0a96adbbebc599100faee6cb90" have entirely different histories.
c844e7932e
...
aa7a5e89ec
@ -1,17 +1,6 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
var _connection
|
|
||||||
|
|
||||||
func _service_discovery():
|
|
||||||
var iq := yield() as Xml.XmlElement
|
|
||||||
print(iq.as_string())
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
_connection = $Connections.establish_new_connection("poto.cafe", "veclavtalica", "-")
|
var connection = $Connections.establish_new_connection("poto.cafe", "veclavtalica", "-")
|
||||||
if _connection == null:
|
if connection == null:
|
||||||
push_error("Connection failed")
|
|
||||||
|
|
||||||
if _connection.push_iq(_connection.domain, "get",
|
|
||||||
"<query xmlns='http://jabber.org/protocol/disco#items'/>",
|
|
||||||
_service_discovery()) != OK:
|
|
||||||
push_error("Connection failed")
|
push_error("Connection failed")
|
||||||
|
@ -6,94 +6,15 @@ class Connection extends Reference:
|
|||||||
var domain: String
|
var domain: String
|
||||||
var bare_jid: String
|
var bare_jid: String
|
||||||
var jid: String
|
var jid: String
|
||||||
var _id_counter: int = 0
|
var id_counter: int = 0
|
||||||
var _pending_iqs: Dictionary # of id to GDScriptFunctionState
|
|
||||||
var _xml_parser := Xml.Parser.new()
|
|
||||||
|
|
||||||
# warning-ignore:unused_signal
|
|
||||||
signal presence_received(presence)
|
|
||||||
# warning-ignore:unused_signal
|
|
||||||
signal message_received(message)
|
|
||||||
# warning-ignore:unused_signal
|
|
||||||
signal iq_received(iq)
|
|
||||||
|
|
||||||
func generate_id() -> int:
|
func generate_id() -> int:
|
||||||
self._id_counter += 1
|
self.id_counter += 1
|
||||||
return hash(self._id_counter)
|
return hash(self.id_counter)
|
||||||
|
|
||||||
func push_iq(to: String, action: String, payload: String, capture: GDScriptFunctionState) -> int: # Error
|
|
||||||
assert(action in ["get", "set"])
|
|
||||||
|
|
||||||
var id := self.generate_id()
|
|
||||||
if self.stream.put_data(
|
|
||||||
"""<iq from='{from}' id='{id}' to='{to}' type='{action}'>{payload}</iq>""".format({
|
|
||||||
"from": self.jid.xml_escape(),
|
|
||||||
"id": id,
|
|
||||||
"to": to.xml_escape(),
|
|
||||||
"action": action,
|
|
||||||
"payload": payload
|
|
||||||
}).to_utf8()) != OK:
|
|
||||||
return ERR_CONNECTION_ERROR
|
|
||||||
|
|
||||||
assert(not id in self._pending_iqs)
|
|
||||||
self._pending_iqs[id] = capture
|
|
||||||
|
|
||||||
return OK
|
|
||||||
|
|
||||||
## Registry of connections used for poking of pending iqs.
|
|
||||||
var _connections: Array # of WeakRef to Connection
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
# todo: Some better interval?
|
|
||||||
if get_tree().connect("physics_frame", self, "_process_connections") != OK:
|
|
||||||
assert("Bruh")
|
|
||||||
|
|
||||||
func _process_connections() -> void:
|
|
||||||
var to_remove := PoolIntArray()
|
|
||||||
|
|
||||||
for idx in range(_connections.size()):
|
|
||||||
var connection := _connections[idx].get_ref() as Connection
|
|
||||||
if connection == null:
|
|
||||||
to_remove.push_back(idx)
|
|
||||||
continue
|
|
||||||
|
|
||||||
var response = _wait_blocking_for_utf8_data(connection.stream, 0)
|
|
||||||
if response == null:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if connection._xml_parser.parse_a_bit(response):
|
|
||||||
var stanza := connection._xml_parser.root
|
|
||||||
if stanza.node_name == "iq":
|
|
||||||
if "to" in stanza.attributes and stanza.attributes["to"] != connection.jid:
|
|
||||||
push_error("Stanza is not addressed to assigned jid")
|
|
||||||
|
|
||||||
if stanza.attributes["type"] in ["result", "error"]:
|
|
||||||
var id := int(stanza.attributes["id"]) # todo: Use strings directly instead?
|
|
||||||
var result = connection._pending_iqs[id].resume(stanza)
|
|
||||||
if result is GDScriptFunctionState:
|
|
||||||
connection._pending_iqs[id] = result
|
|
||||||
elif result != null:
|
|
||||||
assert("Ignored result of iq subroutine: " + result)
|
|
||||||
else:
|
|
||||||
var was_present := connection._pending_iqs.erase(id)
|
|
||||||
assert(was_present)
|
|
||||||
|
|
||||||
elif stanza.attributes["type"] in ["set", "get"]:
|
|
||||||
connection.emit_signal("iq_received", stanza)
|
|
||||||
|
|
||||||
elif stanza.node_name == "message":
|
|
||||||
connection.emit_signal("message_received", stanza)
|
|
||||||
|
|
||||||
elif stanza.node_name == "presence":
|
|
||||||
connection.emit_signal("presence_received", stanza)
|
|
||||||
|
|
||||||
connection._xml_parser = Xml.Parser.new()
|
|
||||||
|
|
||||||
## Collect dropped connections.
|
|
||||||
for idx in range(to_remove.size() - 1, 0, -1):
|
|
||||||
_connections.remove(to_remove[idx])
|
|
||||||
|
|
||||||
func establish_new_connection(domain: String, identity: String, password: String) -> Connection:
|
func establish_new_connection(domain: String, identity: String, password: String) -> Connection:
|
||||||
|
## XMPP uses unidirectional pipes for communication, which means
|
||||||
|
## multiple connections are open over different predefined ports.
|
||||||
var stream := StreamPeerTCP.new()
|
var stream := StreamPeerTCP.new()
|
||||||
if stream.connect_to_host(domain, 5222) != OK:
|
if stream.connect_to_host(domain, 5222) != OK:
|
||||||
push_error("Cannot establish client->server pipe to " + domain)
|
push_error("Cannot establish client->server pipe to " + domain)
|
||||||
@ -115,8 +36,6 @@ func establish_new_connection(domain: String, identity: String, password: String
|
|||||||
if _negotiate_connection(result, password) != OK:
|
if _negotiate_connection(result, password) != OK:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
_connections.push_back(weakref(result))
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# todo: Make it async
|
# todo: Make it async
|
||||||
@ -326,7 +245,7 @@ func _bind_resource(connection: Connection, resource: String = "tochie-facade")
|
|||||||
<resource>{resource}</resource>
|
<resource>{resource}</resource>
|
||||||
</bind></iq>""".format({
|
</bind></iq>""".format({
|
||||||
"id": iq_id,
|
"id": iq_id,
|
||||||
"resource": resource.xml_escape()
|
"resource": resource
|
||||||
}).to_utf8()) != OK:
|
}).to_utf8()) != OK:
|
||||||
return ERR_CONNECTION_ERROR
|
return ERR_CONNECTION_ERROR
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ class XmlElement extends XmlNode:
|
|||||||
return child
|
return child
|
||||||
return null
|
return null
|
||||||
|
|
||||||
func as_string(level: int = 0) -> String:
|
func as_string(level = 0) -> String:
|
||||||
var result = '\t'.repeat(level) + "XmlElement \"%s\"" % self.node_name
|
var result = '\t'.repeat(level) + "XmlElement \"%s\"" % self.node_name
|
||||||
if self.attributes.size() > 0:
|
if self.attributes.size() > 0:
|
||||||
result += ", Attributes: " + String(self.attributes)
|
result += ", Attributes: " + String(self.attributes)
|
||||||
@ -32,12 +32,11 @@ class XmlText extends XmlNode:
|
|||||||
|
|
||||||
func is_text() -> bool: return true
|
func is_text() -> bool: return true
|
||||||
|
|
||||||
func as_string(_level: int = 0) -> String:
|
func as_string(level = 0) -> String:
|
||||||
return "XmlText \"%s\"" % self.data
|
return "XmlText \"%s\"" % self.data
|
||||||
|
|
||||||
## Wrapper over XMLParser for TCP stream oriented XML data.
|
## Wrapper over XMLParser for TCP stream oriented XML data.
|
||||||
# todo: Save namespaces.
|
# todo: Save namespaces.
|
||||||
# todo: Ability to parse from partially arrived data, with saving for future resume.
|
|
||||||
class Parser extends Reference:
|
class Parser extends Reference:
|
||||||
var root: XmlElement = null
|
var root: XmlElement = null
|
||||||
var _element_stack: Array # of XmlElement
|
var _element_stack: Array # of XmlElement
|
||||||
@ -87,11 +86,7 @@ class Parser extends Reference:
|
|||||||
text.data = parser.get_node_data()
|
text.data = parser.get_node_data()
|
||||||
self._element_stack[self._element_stack.size() - 1].children.push_back(text)
|
self._element_stack[self._element_stack.size() - 1].children.push_back(text)
|
||||||
|
|
||||||
elif parser.get_node_type() == XMLParser.NODE_UNKNOWN:
|
|
||||||
## Needed for things like <?xml ?> version specification.
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
push_error("Node type unimplemented")
|
push_warning("Node type unimplemented and ignored")
|
||||||
|
|
||||||
return self._element_stack.size() == 0
|
return self._element_stack.size() == 0
|
||||||
|
Loading…
Reference in New Issue
Block a user