mirror of
https://port.numenaute.org/aleajactaest/bazar_alea.git
synced 2024-10-05 00:08:44 +00:00
660 lines
22 KiB
GDScript
660 lines
22 KiB
GDScript
extends Node
|
|
|
|
# Stream XMPP
|
|
# Author : AleaJactaEst
|
|
#
|
|
# https://xmpp.org/extensions/xep-0178.html
|
|
# https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml
|
|
|
|
|
|
# Boucle
|
|
# - non connecte
|
|
# - connecte en claire
|
|
# - connecte en TLS
|
|
# - lance l'authentification
|
|
# - step authentification
|
|
# - identifie
|
|
|
|
signal send_msg_debug(message:String)
|
|
signal send_msg_error(message:String)
|
|
signal new_stanza(stanza)
|
|
|
|
enum StreamState {
|
|
END, # Stream is over. Starting with this.
|
|
START, # Stream hass started.
|
|
TLS, # Connection has been made and the first header is send. Let's get ssl!
|
|
AUTHENTICATE, # We have negotiated whether to use TLS, let's authenticate.
|
|
STANZA # We have authenticated, account is now allowed to send stanzas
|
|
}
|
|
var stream_status = StreamState.END
|
|
|
|
enum XMPPState {
|
|
NONE, # Nothing is configured
|
|
TRY_CONNECT_SERVER, # Try to connect to server
|
|
CONNECTED_SERVER, # Confirmed we are connected to server
|
|
INTIALIZE, # Initialize first connection with server
|
|
INTIALIZED, # when initialize is finished
|
|
FEATURE_SERVER, # We received Feature (TLS ?)
|
|
START_TLS, # We start TLS
|
|
WAIT_TLS, # Wait TLS Done
|
|
MISSING_TLS, # Missing feature TLS -> wait 30min and retry
|
|
STARTED_TLS, # TLS connection is done
|
|
AUTHENTICATE_STEP_1, # Launch authenticate
|
|
AUTHENTICATE_STEP_2, # Launch authenticate
|
|
AUTHENTICATE_STEP_3, # Launch authenticate
|
|
AUTHENTICATED, # We finished authenticate
|
|
}
|
|
|
|
@export var server_xmpp_name:String = "localhost":
|
|
set = set_server_xmpp_name, get = get_server_xmpp_name
|
|
|
|
func set_server_xmpp_name(value:String):
|
|
server_xmpp_name = value
|
|
|
|
func get_server_xmpp_name() -> String:
|
|
return server_xmpp_name
|
|
|
|
@export var port_number:int = 5222:
|
|
set = set_port_number, get = get_port_number
|
|
|
|
func set_port_number(value:int):
|
|
port_number = value
|
|
|
|
func get_port_number() -> int:
|
|
return port_number
|
|
|
|
@export var account_name:String = "undefined@localhost":
|
|
set = set_account_name, get = get_account_name
|
|
|
|
func set_account_name(value:String):
|
|
account_name = value
|
|
|
|
func get_account_name() -> String:
|
|
return account_name
|
|
|
|
@export var password:String = "undefined":
|
|
set = set_password, get = get_password
|
|
|
|
func set_password(value:String):
|
|
password = value
|
|
|
|
func get_password() -> String:
|
|
return password
|
|
|
|
@export var locale:String = "en":
|
|
set = set_locale, get = get_locale
|
|
|
|
func set_locale(value:String):
|
|
locale = value
|
|
|
|
func get_locale() -> String:
|
|
return locale
|
|
|
|
@export var xmppclient:bool = false:
|
|
get:
|
|
return xmppclient
|
|
set(value):
|
|
xmppclient = value
|
|
|
|
const MAX_WAIT_CONNECTING:float = 60.0
|
|
const MAX_WAIT_MISSING_TLS:float = 1800.0
|
|
|
|
var try_connect:int = 0
|
|
var count_connecting_time:float = MAX_WAIT_CONNECTING
|
|
var partial_stanza:String = ""
|
|
var tcp_peer:StreamPeerTCP = StreamPeerTCP.new()
|
|
var ssl_peer:StreamPeerTLS = StreamPeerTLS.new()
|
|
var tgt_peer = null
|
|
var status:StreamState = StreamState.END
|
|
var xmpp_state = XMPPState.NONE
|
|
var authentication_methods = []
|
|
|
|
func reinit_stream():
|
|
if ssl_peer != null:
|
|
ssl_peer.disconnect_from_stream()
|
|
else:
|
|
ssl_peer = StreamPeerTLS.new()
|
|
if tcp_peer != null:
|
|
tcp_peer.disconnect_from_host()
|
|
else:
|
|
tcp_peer = StreamPeerTCP.new()
|
|
tgt_peer = tcp_peer
|
|
start_stream()
|
|
stream_status = self.StreamState.START
|
|
|
|
|
|
func _init() -> void:
|
|
var language = OS.get_locale()
|
|
set_locale(language)
|
|
tgt_peer = tcp_peer
|
|
# reinit_stream()
|
|
|
|
|
|
func _process(delta) -> void:
|
|
print(server_xmpp_name, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status, " / xmpp_state:", XMPPState.keys()[xmpp_state])
|
|
if xmppclient == false:
|
|
return
|
|
# Wait MAX_WAIT_MISSING_TLS (s) if server can't negociate with TLS
|
|
if xmpp_state == XMPPState.MISSING_TLS:
|
|
if count_connecting_time >= MAX_WAIT_MISSING_TLS:
|
|
xmpp_state = XMPPState.NONE
|
|
else:
|
|
count_connecting_time += delta
|
|
elif xmpp_state == XMPPState.NONE:
|
|
if count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
xmpp_state = XMPPState.TRY_CONNECT_SERVER
|
|
if ssl_peer.get_status() != StreamPeerTLS.STATUS_DISCONNECTED:
|
|
ssl_peer.disconnect_from_stream()
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_NONE:
|
|
tcp_peer.disconnect_from_host()
|
|
else:
|
|
count_connecting_time += delta
|
|
elif xmpp_state == XMPPState.TRY_CONNECT_SERVER:
|
|
if tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.CONNECTED_SERVER
|
|
count_connecting_time = MAX_WAIT_CONNECTING
|
|
elif count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
var res = tcp_peer.connect_to_host(server_xmpp_name, port_number)
|
|
if res == OK:
|
|
send_msg_debug.emit("Send connect_to_host : ok")
|
|
#stream_status = self.StreamState.END
|
|
#xmpp_state = XMPPState.CONNECTED_SERVER
|
|
count_connecting_time = 0
|
|
else:
|
|
send_msg_error.emit("Error to connect to XMPP server (return:%d)" % res)
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
else:
|
|
if (tcp_peer.has_method("poll")):
|
|
tcp_peer.poll()
|
|
count_connecting_time += delta
|
|
elif xmpp_state == XMPPState.CONNECTED_SERVER:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
if count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
count_connecting_time = 0
|
|
send_msg_debug.emit("send_tcp_initialize_xmpp")
|
|
send_tcp_initialize_xmpp()
|
|
else:
|
|
count_connecting_time += delta
|
|
if (tcp_peer.has_method("poll")):
|
|
tcp_peer.poll()
|
|
if tcp_peer.get_available_bytes()>0:
|
|
var response = tcp_peer.get_string(tcp_peer.get_available_bytes())
|
|
send_msg_debug.emit("Stream: response: " + response)
|
|
response = remove_stream_header(response)
|
|
if not analyze_error(response):
|
|
if analyze_feature_starttls(response):
|
|
xmpp_state = XMPPState.START_TLS
|
|
else:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.MISSING_TLS
|
|
elif xmpp_state == XMPPState.START_TLS:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
if count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.NONE
|
|
else:
|
|
count_connecting_time += delta
|
|
if (tcp_peer.has_method("poll")):
|
|
tcp_peer.poll()
|
|
if tcp_peer.get_available_bytes()>0:
|
|
var response = tcp_peer.get_string(tcp_peer.get_available_bytes())
|
|
send_msg_debug.emit("Stream: response: " + response)
|
|
response = remove_stream_header(response)
|
|
if response.begins_with("<proceed"):
|
|
send_msg_debug.emit("Stream: gotten go ahead for ssl")
|
|
var options:TLSOptions = TLSOptions.client_unsafe()
|
|
var ssl_succes = ssl_peer.connect_to_stream(tcp_peer, "localhost", options)
|
|
if ssl_succes == OK:
|
|
send_msg_debug.emit("Stream: switched to SSL!")
|
|
xmpp_state = XMPPState.WAIT_TLS
|
|
else:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.MISSING_TLS
|
|
else:
|
|
send_msg_error.emit(tr("TlS negotiation failed."))
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.MISSING_TLS
|
|
elif xmpp_state == XMPPState.WAIT_TLS:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
if ssl_peer.get_status() == StreamPeerTLS.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.STARTED_TLS
|
|
count_connecting_time = MAX_WAIT_CONNECTING
|
|
elif ssl_peer.get_status() == StreamPeerTLS.STATUS_HANDSHAKING:
|
|
ssl_peer.poll()
|
|
if count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.MISSING_TLS
|
|
else:
|
|
count_connecting_time += delta
|
|
else:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.MISSING_TLS
|
|
elif xmpp_state == XMPPState.STARTED_TLS:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
if ssl_peer.get_status() != StreamPeerTLS.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
if count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
count_connecting_time = 0
|
|
send_msg_debug.emit("send_ssl_initialize_xmpp")
|
|
send_ssl_initialize_xmpp()
|
|
else:
|
|
count_connecting_time += delta
|
|
if (ssl_peer.has_method("poll")):
|
|
ssl_peer.poll()
|
|
if ssl_peer.get_available_bytes()>0:
|
|
var response = ssl_peer.get_string(ssl_peer.get_available_bytes())
|
|
send_msg_debug.emit("Stream: response: " + response)
|
|
response = remove_stream_header(response)
|
|
if not analyze_error(response):
|
|
if analyze_feature_mechanisms(response):
|
|
if authentication_methods.size() > 0:
|
|
count_connecting_time = MAX_WAIT_CONNECTING
|
|
xmpp_state = XMPPState.AUTHENTICATE_STEP_1
|
|
#send_msg_debug.emit("AUTHENTICATE_STEP_1")
|
|
# elif response.begins_with("<success"):
|
|
# stream_status = self.StreamState.STANZA
|
|
if xmpp_state == XMPPState.AUTHENTICATE_STEP_1:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
if count_connecting_time >= MAX_WAIT_CONNECTING:
|
|
count_connecting_time = 0
|
|
negotiate_ssl_sasl(authentication_methods)
|
|
else:
|
|
count_connecting_time += delta
|
|
if (ssl_peer.has_method("poll")):
|
|
ssl_peer.poll()
|
|
if ssl_peer.get_available_bytes()>0:
|
|
var response = ssl_peer.get_string(ssl_peer.get_available_bytes())
|
|
send_msg_debug.emit("Stream: response: " + response)
|
|
response = remove_stream_header(response)
|
|
if response.begins_with("<success"):
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.AUTHENTICATED
|
|
|
|
|
|
func analyze_error(response:String) -> bool:
|
|
if response.begins_with("<stream:error"):
|
|
var stream_error = XMPPStreamError.new()
|
|
stream_error.parse_from_xml(response)
|
|
var error_name = stream_error.error_name_for_enum(stream_error.error_type)
|
|
var error = tr('A stream error of type "%" occured, or in other words: % (Stream errors cannot be recovered from, the connection will be closed.'.format([error_name, stream_error.human_friendly_error_message()], "%)"))
|
|
error.emit(error)
|
|
return true
|
|
return false
|
|
|
|
|
|
func analyze_feature_starttls(response:String) -> bool:
|
|
if response.begins_with("<stream:features"):
|
|
var stream_features = XMPPStreamFeatures.new()
|
|
stream_features.parse_from_xml(response)
|
|
|
|
if stream_features.parsedDictionary.has("starttls"):
|
|
send_msg_debug.emit("Stream: sending request for ssl")
|
|
var request_tls = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
|
|
send_tcp_string(request_tls)
|
|
stream_status = self.StreamState.TLS
|
|
return true
|
|
return false
|
|
|
|
|
|
func analyze_feature_mechanisms(response:String) -> bool:
|
|
if response.begins_with("<stream:features"):
|
|
var stream_features = XMPPStreamFeatures.new()
|
|
stream_features.parse_from_xml(response)
|
|
if stream_features.parsedDictionary.has("mechanisms"):
|
|
print("response:", response)
|
|
authentication_methods = stream_features.parsedDictionary["mechanisms"]
|
|
if (authentication_methods.size() > 0):
|
|
var tmp:String = ""
|
|
var sep:String = ""
|
|
for item in authentication_methods:
|
|
tmp += sep + item
|
|
sep = ", "
|
|
send_msg_debug.emit("Stream: authentication methods: " + tmp)
|
|
#negotiate_ssl_sasl(authentication_methods)
|
|
#stream_status = self.StreamState.AUTHENTICATE
|
|
return true
|
|
#negotiate_ssl_sasl(authentication_methods)
|
|
return false
|
|
|
|
|
|
func send_tcp_initialize_xmpp() -> void:
|
|
""" Send the stream header.
|
|
Needs to be done several times over stream negotiation.
|
|
"""
|
|
var message:String = "<?xml version='1.0'?>" + \
|
|
"<stream:stream" + \
|
|
" xmlns=\"jabber:client\"" + \
|
|
" version=\"1.0\"" + \
|
|
" xmlns:stream=\"http://etherx.jabber.org/streams\"" + \
|
|
" to=\"" + server_xmpp_name + "\" " + \
|
|
" xml:lang=\"en\"" + \
|
|
" > "
|
|
# " from=\"" + account_name + "\" " + \
|
|
# + locale + "'" +
|
|
print(message)
|
|
send_tcp_string(message)
|
|
|
|
|
|
func send_ssl_initialize_xmpp() -> void:
|
|
""" Send the stream header.
|
|
Needs to be done several times over stream negotiation.
|
|
"""
|
|
var server_name = account_name.split("@")[-1]
|
|
var message:String = "<?xml version='1.0'?>" + \
|
|
"<stream:stream" + \
|
|
" xmlns=\"jabber:client\"" + \
|
|
" version=\"1.0\"" + \
|
|
" xmlns:stream=\"http://etherx.jabber.org/streams\"" + \
|
|
" from=\"" + account_name + "\" " + \
|
|
" to=\"" + server_name + "\" " + \
|
|
" xml:lang=\"en\"" + \
|
|
" > "
|
|
# " from=\"" + account_name + "\" " + \
|
|
# + locale + "'" +
|
|
print(message)
|
|
send_ssl_string(message)
|
|
|
|
|
|
|
|
func negotiate_ssl_sasl(authentication_methods : Array) -> bool:
|
|
if ( authentication_methods.has("PLAIN")):
|
|
send_msg_debug.emit("Stream: sending request for plain")
|
|
var msg:PackedByteArray = PackedByteArray()
|
|
msg.push_back(0)
|
|
var t = account_name.split("@")[0]
|
|
msg += t.to_ascii_buffer()
|
|
#msg += conv_string_to_PackedByteArray(account_name.split("@")[0])
|
|
msg.push_back(0)
|
|
msg += password.to_ascii_buffer()
|
|
var auth_account:String = Marshalls.raw_to_base64(msg)
|
|
var request_sasl:String = "<auth" + \
|
|
" xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \
|
|
" mechanism='PLAIN'" + \
|
|
" >" + auth_account + "</auth>"
|
|
send_ssl_string(request_sasl)
|
|
return true
|
|
else:
|
|
send_msg_error.emit("Impossible to authenticate (unknown protocol)")
|
|
return false
|
|
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
func connect_to_server_xmpp() -> void:
|
|
var res = tcp_peer.connect_to_host(server_xmpp_name, port_number)
|
|
if res == OK:
|
|
count_connecting_time = 0
|
|
stream_status = self.StreamState.END
|
|
set_process(true)
|
|
tgt_peer = tcp_peer
|
|
|
|
func _process_old(delta) -> void:
|
|
print(server_xmpp_name, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status)
|
|
if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
|
|
print("STATUS_CONNECTED:", delta)
|
|
if (tgt_peer.has_method("poll")):
|
|
tgt_peer.poll()
|
|
if tgt_peer.get_available_bytes()>0:
|
|
var response = tgt_peer.get_string(tgt_peer.get_available_bytes())
|
|
send_msg_debug.emit("Stream: response: " + response)
|
|
if stream_status == self.StreamState.STANZA:
|
|
#collect_stanza(remove_stream_header(response))
|
|
print("collect_stanza")
|
|
else:
|
|
print("stream_process")
|
|
stream_process(remove_stream_header(response))
|
|
# print(remove_stream_header(response))
|
|
elif stream_status == self.StreamState.END:
|
|
reinit_stream()
|
|
#start_stream()
|
|
#stream_status = self.StreamState.START
|
|
count_connecting_time = 0
|
|
if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTING:
|
|
print("STATUS_CONNECTING:", delta)
|
|
count_connecting_time += delta
|
|
if (tgt_peer.has_method("poll")):
|
|
tgt_peer.poll()
|
|
if count_connecting_time > 60: # (1 -> 1s) if it took more than 1s to connect, error
|
|
print("**** Stream: Stuck connecting, will now disconnect")
|
|
Global.msg_error("Stream: Stuck connecting, will now disconnect", [])
|
|
send_msg_debug.emit("Stream: Stuck connecting, will now disconnect")
|
|
tgt_peer.disconnect_from_host() #interrupts connection to nothing
|
|
set_process(false) # stop listening for packets
|
|
try_connect = 20
|
|
stream_status = self.StreamState.END
|
|
reinit_stream()
|
|
if tgt_peer.get_status() == StreamPeerTCP.STATUS_NONE and xmppclient:
|
|
print("connect_to_server:", xmppclient)
|
|
connect_to_server()
|
|
xmppclient = false
|
|
|
|
|
|
func connect_to_server():
|
|
""" Connect to the server ip and port, and start checking whether there's stream info yet. """
|
|
if tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
|
|
pass
|
|
if try_connect == 0 and tcp_peer.get_status() == StreamPeerTCP.STATUS_NONE:
|
|
#print("-> ", server_ip, ":", port_number, "/" , tcp_peer.get_status())
|
|
var res = tcp_peer.connect_to_host(server_xmpp_name, port_number)
|
|
if res == OK:
|
|
count_connecting_time = 0
|
|
stream_status = self.StreamState.END
|
|
set_process(true)
|
|
tgt_peer = tcp_peer
|
|
else:
|
|
try_connect = 20
|
|
else:
|
|
try_connect -= 1
|
|
|
|
|
|
func send_string(stanza:String) ->void:
|
|
""" Send a string in the appropriate encoding. """
|
|
send_msg_debug.emit("Sending data: '%'".format([stanza], "%"))
|
|
tgt_peer.put_data(stanza.to_utf8_buffer())
|
|
|
|
|
|
func send_tcp_string(stanza:String) ->void:
|
|
""" Send a string in the appropriate encoding. """
|
|
send_msg_debug.emit("Sending data: '%'".format([stanza], "%"))
|
|
tcp_peer.put_data(stanza.to_utf8_buffer())
|
|
|
|
|
|
func send_ssl_string(stanza:String) ->void:
|
|
""" Send a string in the appropriate encoding. """
|
|
send_msg_debug.emit("Sending data: '%'".format([stanza], "%"))
|
|
ssl_peer.put_data(stanza.to_utf8_buffer())
|
|
|
|
|
|
func conv_string_to_PackedByteArray(message:String) -> PackedByteArray:
|
|
return message.to_ascii_buffer()
|
|
|
|
|
|
#func send_PackedByteArray(message:PackedByteArray) -> void:
|
|
# """ Send a PackedByteArray """
|
|
# send_msg_debug.emit("Sending data: '" + message.get_string_from_utf8() + "'")
|
|
# tgt_peer.put_data(message)
|
|
|
|
|
|
func start_stream() -> void:
|
|
""" Send the stream header.
|
|
Needs to be done several times over stream negotiation.
|
|
"""
|
|
var server_name = account_name.split("@")[-1]
|
|
var message:String = "<?xml version='1.0'?>" + \
|
|
"<stream:stream" + \
|
|
" xmlns=\"jabber:client\"" + \
|
|
" version=\"1.0\"" + \
|
|
" xmlns:stream=\"http://etherx.jabber.org/streams\"" + \
|
|
" from=\"" + account_name + "\" " + \
|
|
" to=\"" + server_name + "\" " + \
|
|
" xml:lang=\"en\"" + \
|
|
" > "
|
|
# " from=\"" + account_name + "\" " + \
|
|
# + locale + "'" +
|
|
print(message)
|
|
send_string(message)
|
|
|
|
func remove_stream_header(text :String) -> String:
|
|
var index = 0
|
|
if text.begins_with("<?") :
|
|
# Strip xml header
|
|
index = text.find("?>")
|
|
text = text.substr(index+2).strip_edges()
|
|
# strip stream header
|
|
var rg = RegEx.new()
|
|
rg.compile("<\\s?(stream|stream:stream)\\s")
|
|
var result = rg.search(text)
|
|
if result:
|
|
send_msg_debug.emit("Stream: Response header received")
|
|
index = text.find(">", result.get_end())
|
|
text = text.substr(index+1)
|
|
return text
|
|
|
|
|
|
func stream_process(response :String = "") -> void:
|
|
""" Try to authenticate using SASL. We can currently only do plain.
|
|
For SCRAM based methods, we need HMAC at the very least.
|
|
"""
|
|
|
|
if (!response.length() == 0 and stream_status != self.StreamState.STANZA):
|
|
if response.begins_with("<stream:error"):
|
|
var stream_error = XMPPStreamError.new()
|
|
stream_error.parse_from_xml(response)
|
|
var error_name = stream_error.error_name_for_enum(stream_error.error_type)
|
|
var error = tr('A stream error of type "%" occured, or in other words: % \n\nStream errors cannot be recovered from, the connection will be closed.'.format([error_name, stream_error.human_friendly_error_message()], "%"))
|
|
error.emit(error)
|
|
|
|
elif response.begins_with("<stream:features"):
|
|
var stream_features = XMPPStreamFeatures.new()
|
|
stream_features.parse_from_xml(response)
|
|
|
|
if stream_features.parsedDictionary.has("starttls"):
|
|
send_msg_debug.emit("Stream: sending request for ssl")
|
|
var request_tls = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
|
|
send_string(request_tls)
|
|
stream_status = self.StreamState.TLS
|
|
|
|
elif stream_features.parsedDictionary.has("mechanisms"):
|
|
print("response:", response)
|
|
var authentication_methods = stream_features.parsedDictionary["mechanisms"]
|
|
if (authentication_methods.size() > 0):
|
|
var tmp:String = ""
|
|
var sep:String = ""
|
|
for item in authentication_methods:
|
|
tmp += sep + item
|
|
sep = ", "
|
|
send_msg_debug.emit("Stream: authentication methods: " + tmp)
|
|
negotiate_sasl(authentication_methods)
|
|
stream_status = self.StreamState.AUTHENTICATE
|
|
|
|
else:
|
|
if stream_status == StreamState.TLS:
|
|
if response.begins_with("<proceed"):
|
|
send_msg_debug.emit("Stream: gotten go ahead for ssl")
|
|
negotiate_tls()
|
|
else:
|
|
send_msg_error.emit(tr("Tls negotiation failed."))
|
|
|
|
elif stream_status == StreamState.AUTHENTICATE:
|
|
if response.begins_with("<success"):
|
|
stream_status = self.StreamState.STANZA
|
|
# start_stream()
|
|
return
|
|
else:
|
|
var parser = XMLParser.new()
|
|
# parser.open_buffer(response.to_utf8())
|
|
var failure_text = []
|
|
while parser.read() == OK:
|
|
if parser.get_node_type() == XMLParser.NODE_TEXT:
|
|
failure_text.append(parser.get_node_data())
|
|
print(failure_text)
|
|
# failure_text = tr("Authentication failed! %".format([PoolStringArray(failure_text).join(" ")], "%"))
|
|
var msg:String = ""
|
|
for item in failure_text:
|
|
msg += item
|
|
send_msg_error.emit(msg)
|
|
elif stream_status == self.StreamState.START:
|
|
connect_to_server()
|
|
start_stream()
|
|
else:
|
|
print("Mystery stream status: "+str(stream_status))
|
|
|
|
|
|
func negotiate_tls() -> bool:
|
|
#var ssl_peer = StreamPeerTLS.new()
|
|
# I am unsure how to validate the ssl certificate for an unknown host?
|
|
#var ssl_succes = FAILED
|
|
var options:TLSOptions = TLSOptions.client_unsafe()
|
|
print("accept_stream")
|
|
#var ssl_succes = ssl_peer.accept_stream(tcp_peer, options)
|
|
var ssl_succes = ssl_peer.connect_to_stream(tcp_peer, "localhost", options)
|
|
print("resultat:", ssl_succes)
|
|
if ssl_succes == OK:
|
|
send_msg_debug.emit("Stream: switched to SSL!")
|
|
print("get_status:", ssl_peer.get_status())
|
|
# Wait connection done
|
|
while ssl_peer.get_status() == StreamPeerTLS.STATUS_HANDSHAKING:
|
|
ssl_peer.poll()
|
|
print("get_status:", ssl_peer.get_status())
|
|
tgt_peer = ssl_peer
|
|
start_stream()
|
|
print("get_status:", ssl_peer.get_status())
|
|
print("-----")
|
|
#stream_status == StreamState.START
|
|
return true
|
|
else:
|
|
send_msg_error.emit("SSL failed, error %".format([ssl_succes], "%"))
|
|
return false
|
|
|
|
|
|
func negotiate_sasl(authentication_methods : Array) -> void:
|
|
if ( authentication_methods.has("PLAIN")):
|
|
send_msg_debug.emit("Stream: sending request for plain")
|
|
var msg:PackedByteArray = PackedByteArray()
|
|
msg.push_back(0)
|
|
var t = account_name.split("@")[0]
|
|
msg += t.to_ascii_buffer()
|
|
#msg += conv_string_to_PackedByteArray(account_name.split("@")[0])
|
|
msg.push_back(0)
|
|
msg += password.to_ascii_buffer()
|
|
var auth_account:String = Marshalls.raw_to_base64(msg)
|
|
var request_sasl:String = "<auth" + \
|
|
" xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \
|
|
" mechanism='PLAIN'" + \
|
|
" >" + auth_account + "</auth>"
|
|
send_string(request_sasl)
|
|
else:
|
|
send_msg_error.emit("Impossible to authenticate (unknown protocol)")
|
|
end_stream()
|
|
|
|
|
|
func end_stream() -> void:
|
|
""" End the stream """
|
|
send_msg_debug.emit("Stream: Ending stream")
|
|
send_string("</stream>")
|
|
if tcp_peer.has_method("disconnect_from_stream"):
|
|
tcp_peer.disconnect_from_stream()
|
|
else:
|
|
tcp_peer.disconnect_from_host()
|
|
set_process(false)
|
|
stream_status = StreamState.END
|