mirror of
https://port.numenaute.org/aleajactaest/bazar_alea.git
synced 2024-10-04 15:58:31 +00:00
1288 lines
46 KiB
GDScript
1288 lines
46 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
|
|
# https://datatracker.ietf.org/doc/html/rfc6120#section-6.5
|
|
|
|
|
|
# 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 chat_connected()
|
|
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
|
|
SELECT_MECHANISM_AUTHENTICATE,
|
|
AUTHENTICATE_STEP_PLAIN, # Select mechanism authenticate PLAIN
|
|
AUTHENTICATE_STEP_DIGEST_MD5,
|
|
# AUTHENTICATE_STEP_1, # Launch authenticate
|
|
# AUTHENTICATE_STEP_2, # Launch authenticate
|
|
# AUTHENTICATE_STEP_3, # Launch authenticate
|
|
# AUTHENTICATE_CHECK_RESULT, # Last time check result
|
|
AUTHENTICATE_FAILURE,
|
|
AUTHENTICATE_SUCCESS,
|
|
AUTHENTICATED, # We finished authenticate
|
|
NOT_AUTHORIZED, # Not Authorize
|
|
ACCOUNT_DISABLED, # Account Disable
|
|
SENDED_START_STREAM,
|
|
}
|
|
|
|
enum ResultAuthenticate {
|
|
NONE, # Authenticate not started
|
|
DONE, # Authenticate finished and done
|
|
ABORT, # Authenticate finished with error (try other method)
|
|
NEXT, # Authenticate need execute other step
|
|
}
|
|
|
|
enum SASLReturn {
|
|
SUCCESS,
|
|
ABORTED,
|
|
ACCOUNT_DISABLED,
|
|
CREDENTIALS_EXPIRED,
|
|
ENCRYPTION_REQUIRED,
|
|
INCORRECT_ENCODING,
|
|
INVALID_AUTHZID,
|
|
INVALID_MECHANISM,
|
|
MALFORMED_REQUEST,
|
|
MECHANISM_TOO_WEAK,
|
|
NOT_AUTHORIZED,
|
|
TEMPORARY_AUTH_FAILURE,
|
|
UNKNOWN
|
|
}
|
|
|
|
@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
|
|
const MAX_WAIT_ACCOUNT_DISABLED:float = 900.0
|
|
const MAX_WAIT_NOT_AUTHORIZED:float = 18000.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 = []
|
|
var selected_mechanism_authenticate:String = ""
|
|
var order_preference_mechanism_authenticate: Array = ['PLAIN']
|
|
#var order_preference_mechanism_authenticate: Array = ['DIGEST-MD5']
|
|
var banned_mechanism_authenticate:Array = ['DIGEST-MD5']
|
|
#DIGEST-MD5, PLAIN, SCRAM-SHA-512-PLUS, SCRAM-SHA-512, SCRAM-SHA-256-PLUS, SCRAM-SHA-256, SCRAM-SHA-1-PLUS, SCRAM-SHA-1, X-OAUTH2
|
|
var auhtentification_step:int = 0
|
|
var auhtentification_challenge:Array = []
|
|
|
|
const listcar:String = "AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn0123456789"
|
|
|
|
#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 _ready():
|
|
randomize()
|
|
|
|
var n1:PackedByteArray = set_PackedByteArray("Mufasa:testrealm@host.com:Circle Of Life")
|
|
var n2:PackedByteArray = hash_md5(n1)
|
|
print("n2:", n2.hex_encode())
|
|
var exemple = digest_md5( "Mufasa", "Circle Of Life", "testrealm@host.com", "dcd98b7102dd2f0e8b11d0f600bfb0c093", "/dir/index.html", "auth", "00000001", "0a4f113b", "utf-8", "md5-sess" )
|
|
print("exemple:", exemple)
|
|
|
|
|
|
exemple = digest_md5( "rob", "Circle Of Life", "cataclysm.cx", "OA6MG9tEQGm2hh", "xmpp/
|
|
cataclysm.cx", "auth", "00000001", "OA6MHXh6VqTrRk", "utf-8", "md5-sess" )
|
|
print("exemple 2:", exemple)
|
|
|
|
|
|
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.NOT_AUTHORIZED:
|
|
if count_connecting_time >= MAX_WAIT_NOT_AUTHORIZED:
|
|
xmpp_state = XMPPState.NONE
|
|
else:
|
|
count_connecting_time += delta
|
|
elif xmpp_state == XMPPState.ACCOUNT_DISABLED:
|
|
if count_connecting_time >= MAX_WAIT_ACCOUNT_DISABLED:
|
|
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
|
|
elif response.begins_with("failure"):
|
|
send_msg_error.emit(tr("TlS negotiation failed."))
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.MISSING_TLS
|
|
else:
|
|
send_msg_error.emit(tr("TlS negotiation failed. (unknow return)"))
|
|
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.SELECT_MECHANISM_AUTHENTICATE
|
|
#send_msg_debug.emit("AUTHENTICATE_STEP_1")
|
|
# elif response.begins_with("<success"):
|
|
# stream_status = self.StreamState.STANZA
|
|
if xmpp_state == XMPPState.SELECT_MECHANISM_AUTHENTICATE:
|
|
select_mechanism_authenticate(authentication_methods)
|
|
if selected_mechanism_authenticate == "NONE":
|
|
send_msg_error.emit("Stream: Impossible to find mechanism to authenticate")
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
return
|
|
elif selected_mechanism_authenticate == "PLAIN":
|
|
xmpp_state = XMPPState.AUTHENTICATE_STEP_PLAIN
|
|
auhtentification_step = 0
|
|
elif selected_mechanism_authenticate == "DIGEST-MD5":
|
|
xmpp_state = XMPPState.AUTHENTICATE_STEP_DIGEST_MD5
|
|
auhtentification_step = 0
|
|
auhtentification_challenge = []
|
|
if xmpp_state == XMPPState.AUTHENTICATE_STEP_PLAIN:
|
|
# https://datatracker.ietf.org/doc/html/rfc4616
|
|
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_authenticate_plain()
|
|
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)
|
|
var res:SASLReturn = get_result_authenticate(response)
|
|
if res == SASLReturn.SUCCESS:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.AUTHENTICATED
|
|
elif res == SASLReturn.NOT_AUTHORIZED:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.NOT_AUTHORIZED
|
|
elif res == SASLReturn.ACCOUNT_DISABLED:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.ACCOUNT_DISABLED
|
|
elif res == SASLReturn.NOT_AUTHORIZED:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
elif res == SASLReturn.INVALID_MECHANISM:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
else:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
if xmpp_state == XMPPState.AUTHENTICATE_STEP_DIGEST_MD5:
|
|
# https://datatracker.ietf.org/doc/html/rfc2831
|
|
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_authenticate_digest_md5()
|
|
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("<challenge"):
|
|
var data:String = get_xml_data(response, ["challenge"])
|
|
#var xmlns:String = get_xml_attribute(response, ["challenge"], "xmlns")
|
|
print(auhtentification_step, " / ", data)
|
|
auhtentification_challenge.append(data)
|
|
if ! negotiate_ssl_authenticate_digest_md5():
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
return
|
|
else:
|
|
var res:SASLReturn = get_result_authenticate(response)
|
|
if res == SASLReturn.SUCCESS:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.AUTHENTICATED
|
|
elif res == SASLReturn.NOT_AUTHORIZED:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.NOT_AUTHORIZED
|
|
elif res == SASLReturn.ACCOUNT_DISABLED:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.ACCOUNT_DISABLED
|
|
elif res == SASLReturn.NOT_AUTHORIZED:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
elif res == SASLReturn.INVALID_MECHANISM:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
else:
|
|
count_connecting_time = 0
|
|
xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE
|
|
banned_mechanism_authenticate.append(selected_mechanism_authenticate)
|
|
if xmpp_state == XMPPState.AUTHENTICATED:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
#send_msg_debug.emit("Stream: Lost flow 1")
|
|
#send_msg_debug.emit("Stream: Lost flow 1 : " + str(tcp_peer.get_status()) )
|
|
return
|
|
if (ssl_peer.has_method("poll")):
|
|
ssl_peer.poll()
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
#send_msg_debug.emit("Stream: Lost flow 2 : " + str(tcp_peer.get_status()) )
|
|
return
|
|
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)
|
|
start_stream()
|
|
if xmpp_state == XMPPState.SENDED_START_STREAM:
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
#send_msg_debug.emit("Stream: Lost flow 1")
|
|
#send_msg_debug.emit("Stream: Lost flow 1 : " + str(tcp_peer.get_status()) )
|
|
return
|
|
if (ssl_peer.has_method("poll")):
|
|
ssl_peer.poll()
|
|
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
|
xmpp_state = XMPPState.NONE
|
|
count_connecting_time = 0
|
|
#send_msg_debug.emit("Stream: Lost flow 2 : " + str(tcp_peer.get_status()) )
|
|
return
|
|
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):
|
|
analyze_stream(response)
|
|
# chat_connected.emit()
|
|
|
|
|
|
func get_xml_data(response:String, nodes:Array) -> String:
|
|
var node_name:String = ""
|
|
var data:String = ""
|
|
var parser:XMLParser = XMLParser.new()
|
|
var cur:Array = []
|
|
parser.open_buffer(response.to_utf8_buffer())
|
|
while (parser.read() == OK):
|
|
if (parser.get_node_type()== XMLParser.NODE_ELEMENT):
|
|
node_name = parser.get_node_name()
|
|
cur.append(node_name)
|
|
print("node_name:", node_name)
|
|
var attributes_dict = {}
|
|
for idx in range(parser.get_attribute_count()):
|
|
attributes_dict[parser.get_attribute_name(idx)] = parser.get_attribute_value(idx)
|
|
print("The ", node_name, " element has the following attributes: ", attributes_dict)
|
|
elif (parser.get_node_type()== XMLParser.NODE_TEXT):
|
|
var correctpath:bool = true
|
|
print(nodes.size() , " != ", cur.size())
|
|
if nodes.size() != cur.size():
|
|
continue
|
|
for i in range(0, nodes.size()):
|
|
print(cur[i] , " != ", nodes[i])
|
|
if cur[i] != nodes[i]:
|
|
correctpath = false
|
|
break
|
|
if correctpath:
|
|
data = parser.get_node_data()
|
|
print("data:", data)
|
|
return data
|
|
elif (parser.get_node_type()== XMLParser.NODE_ELEMENT_END):
|
|
cur.pop_back()
|
|
return ""
|
|
|
|
|
|
func get_xml_attribute(response:String, nodes:Array, attribute:String) -> String:
|
|
var node_name:String = ""
|
|
var data:String = ""
|
|
var parser:XMLParser = XMLParser.new()
|
|
var cur:Array = []
|
|
parser.open_buffer(response.to_utf8_buffer())
|
|
while (parser.read() == OK):
|
|
if (parser.get_node_type()== XMLParser.NODE_ELEMENT):
|
|
node_name = parser.get_node_name()
|
|
cur.append(node_name)
|
|
print("node_name:", node_name)
|
|
var attributes_dict = {}
|
|
for idx in range(parser.get_attribute_count()):
|
|
if parser.get_attribute_name(idx) == attribute:
|
|
return parser.get_attribute_value(idx)
|
|
elif (parser.get_node_type()== XMLParser.NODE_ELEMENT_END):
|
|
cur.pop_back()
|
|
return ""
|
|
|
|
|
|
func get_result_authenticate(response:String) -> SASLReturn:
|
|
if response.begins_with("<success"):
|
|
count_connecting_time = 0
|
|
return SASLReturn.SUCCESS
|
|
elif response.begins_with("<failure"):
|
|
if response.contains("<aborted/>"):
|
|
return SASLReturn.ABORTED
|
|
elif response.contains("<account-disabled/>"):
|
|
return SASLReturn.ACCOUNT_DISABLED
|
|
if response.contains("<credentials-expired/>"):
|
|
return SASLReturn.CREDENTIALS_EXPIRED
|
|
if response.contains("<encryption-required/>"):
|
|
return SASLReturn.ENCRYPTION_REQUIRED
|
|
if response.contains("<incorrect-encoding/>"):
|
|
return SASLReturn.INCORRECT_ENCODING
|
|
if response.contains("<invalid-authzid/>"):
|
|
return SASLReturn.INVALID_AUTHZID
|
|
if response.contains("<invalid-mechanism/>"):
|
|
return SASLReturn.INVALID_MECHANISM
|
|
if response.contains("<malformed-request/>"):
|
|
return SASLReturn.MALFORMED_REQUEST
|
|
if response.contains("<mechanism-too-weak/>"):
|
|
return SASLReturn.MECHANISM_TOO_WEAK
|
|
if response.contains("<not-authorized/>"):
|
|
return SASLReturn.NOT_AUTHORIZED
|
|
if response.contains("<temporary-auth-failure/>"):
|
|
return SASLReturn.TEMPORARY_AUTH_FAILURE
|
|
return SASLReturn.UNKNOWN
|
|
|
|
|
|
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:String = 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()], "%)"))
|
|
send_msg_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 select_mechanism_authenticate(authentication_methods : Array) -> void:
|
|
selected_mechanism_authenticate = "NONE"
|
|
for item in order_preference_mechanism_authenticate:
|
|
if banned_mechanism_authenticate.has(item):
|
|
continue
|
|
elif authentication_methods.has(item):
|
|
selected_mechanism_authenticate = item
|
|
break
|
|
|
|
|
|
func negotiate_ssl_authenticate_plain() -> void:
|
|
send_msg_debug.emit("Stream: sending request for PLAIN")
|
|
var msg:PackedByteArray = PackedByteArray()
|
|
# [authzid]
|
|
#msg += server_xmpp_name.to_ascii_buffer()
|
|
# UTF8NUL
|
|
msg.push_back(0)
|
|
# authcid
|
|
var t:String = account_name.split("@")[0]
|
|
msg += t.to_ascii_buffer()
|
|
# UTF8NUL
|
|
msg.push_back(0)
|
|
# passwd
|
|
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)
|
|
|
|
|
|
func hash_md5(data:PackedByteArray ) -> PackedByteArray:
|
|
var ctx = HashingContext.new()
|
|
ctx.start(HashingContext.HASH_MD5)
|
|
ctx.update(data)
|
|
var res = ctx.finish()
|
|
# Print the result as hex string and array.
|
|
printt("hash_md5:", res.hex_encode(), Array(res))
|
|
return res
|
|
|
|
|
|
func set_PackedByteArray(data:String) -> PackedByteArray:
|
|
# var ret:PackedByteArray = PackedByteArray()
|
|
# for item in data:
|
|
# print(item)
|
|
return data.to_ascii_buffer()
|
|
|
|
|
|
func digest_md5(username:String, password:String, realm:String, nonce:String, uri:String, qop:String, nc:String, cnonce:String, charset:String, algorithm:String) -> String:
|
|
if qop != "auth" and qop != "auth-int":
|
|
send_msg_error.emit("Stream: Authenticate digest-md5, qop unknonw (%s)" % qop)
|
|
return ""
|
|
if charset != "utf-8":
|
|
send_msg_error.emit("Stream: Authenticate digest-md5, charset unknonw (%s)" % charset)
|
|
return ""
|
|
if algorithm != "md5-sess":
|
|
send_msg_error.emit("Stream: Authenticate digest-md5, algorithm unknonw (%s)" % algorithm)
|
|
return ""
|
|
#var serv_type:String = "xmpp"
|
|
|
|
var X:String = username + ":" + realm + ":" + password
|
|
print("X:", X)
|
|
var Y:String = hash_md5(set_PackedByteArray(X)).hex_encode()
|
|
print("Y:", Y)
|
|
var A1:String = Y + ":" + nonce + ":" + cnonce
|
|
print("A1:", A1)
|
|
var A2:String = "AUTHENTICATE:" + uri
|
|
print("A2:", A2)
|
|
var HA1:String = hash_md5(set_PackedByteArray(A1)).hex_encode()
|
|
print("HA1:", HA1)
|
|
var HA2:String = hash_md5(set_PackedByteArray(A2)).hex_encode()
|
|
print("HA2:", HA2)
|
|
var KD:String = HA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + HA2
|
|
print("KD:", KD)
|
|
var Z:String = hash_md5(set_PackedByteArray(KD)).hex_encode()
|
|
print("Z:", Z)
|
|
#
|
|
# var HA1a:PackedByteArray = set_PackedByteArray(username + ":" + realm + ":" + password)
|
|
# print("HA1a:", HA1a.get_string_from_ascii())
|
|
# #var HA1b:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + cnonce)
|
|
# #print("HA1b:", HA1b.hex_encode())
|
|
# var HA1c:PackedByteArray = hash_md5(HA1a)
|
|
# print("HA1c:", HA1c.hex_encode())
|
|
# var HA1d:PackedByteArray = hash_md5(set_PackedByteArray(HA1c.hex_encode() + ":" + nonce + ":" + cnonce))
|
|
# print("HA1d:", HA1d.hex_encode())
|
|
# var method:String = "AUTHENTICATE:"
|
|
# var HA2:PackedByteArray = PackedByteArray()
|
|
# if qop == "auth":
|
|
# HA2 = hash_md5(set_PackedByteArray(method + ":" + uri))
|
|
# print("HA2:", HA2.hex_encode())
|
|
# else:
|
|
# # HA2 = MD5(method:digestURI:MD5(entityBody))
|
|
# var entityBody:String = ""
|
|
# var u:PackedByteArray = hash_md5(set_PackedByteArray(entityBody))
|
|
# #var t1:PackedByteArray = set_PackedByteArray(method + ":" + uri + ":")
|
|
# var t:PackedByteArray = set_PackedByteArray(method + ":" + uri + ":" + u.hex_encode())
|
|
# HA2 = hash_md5(t)
|
|
# #var i1:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":")
|
|
# var response:PackedByteArray = hash_md5(set_PackedByteArray(
|
|
# HA1d.hex_encode() +
|
|
# ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" +
|
|
# HA2.hex_encode()))
|
|
var ifrealm:String = ""
|
|
if realm != "":
|
|
ifrealm = ",realm=" + realm
|
|
var msg: String = \
|
|
"username=\"" + username + "\"" + \
|
|
ifrealm + \
|
|
",nonce=\"" + nonce + "\"" + \
|
|
",cnonce=\"" + cnonce + "\"" + \
|
|
",nc=" + nc + \
|
|
",qop=" + qop + \
|
|
",digest-uri=\"" + uri + "\"" + \
|
|
",response=" + Z + \
|
|
",charset=\"" + charset + "\""
|
|
print(msg)
|
|
return Marshalls.utf8_to_base64(msg)
|
|
|
|
|
|
func negotiate_ssl_authenticate_digest_md5() -> bool:
|
|
""" NE MARCHE PAS !!!!!! """
|
|
send_msg_debug.emit("Stream: sending request for DIGEST-MD5 (step:%d)" % auhtentification_step)
|
|
if auhtentification_step == 0:
|
|
var request_sasl:String = "<auth" + \
|
|
" xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \
|
|
" mechanism='DIGEST-MD5'" + \
|
|
" >" + "</auth>"
|
|
send_ssl_string(request_sasl)
|
|
auhtentification_step += 1
|
|
return true
|
|
elif auhtentification_step == 1:
|
|
var tmp:String = Marshalls.base64_to_utf8(auhtentification_challenge[0])
|
|
var tab:Array = tmp.split(",")
|
|
var realm:String = ""
|
|
var nonce:String = ""
|
|
var qop:String = ""
|
|
var charset:String = ""
|
|
var algorithm:String = ""
|
|
var cnonce:String = ""
|
|
var nc:String = "00000001"
|
|
var username:String = account_name.split("@")[0]
|
|
#var username:String = account_name
|
|
print(str(tab))
|
|
for item in tab:
|
|
var tab2:Array = item.split("=")
|
|
var key:String = tab2[0]
|
|
var value:String = tab2[1]
|
|
if key == "realm":
|
|
realm = value.trim_prefix('"').trim_suffix('"')
|
|
if key == "nonce":
|
|
nonce = value.trim_prefix('"').trim_suffix('"')
|
|
elif key == "qop":
|
|
qop = value.trim_prefix('"').trim_suffix('"')
|
|
elif key == "charset":
|
|
charset = value
|
|
elif key == "algorithm":
|
|
algorithm = value
|
|
print(key," = ", value)
|
|
print(tmp, " ", qop, " ", charset, " ",nonce)
|
|
if qop != "auth" and qop != "auth-int":
|
|
send_msg_error.emit("Stream: Authenticate digest-md5, qop unknonw (%s)" % qop)
|
|
return false
|
|
if charset != "utf-8":
|
|
send_msg_error.emit("Stream: Authenticate digest-md5, charset unknonw (%s)" % charset)
|
|
return false
|
|
if algorithm != "md5-sess":
|
|
send_msg_error.emit("Stream: Authenticate digest-md5, algorithm unknonw (%s)" % algorithm)
|
|
return false
|
|
for car in nonce:
|
|
var v:String = listcar[randi() % listcar.length()]
|
|
cnonce += v
|
|
print(cnonce)
|
|
var auth_account = digest_md5(username,password,realm,nonce, "xmpp/" + server_xmpp_name, qop, nc, cnonce, charset, algorithm)
|
|
# print(tmp)
|
|
# # username:String, password:String, realm:String, nonce:String, uri:String, qop:String, nc:String, cnonce:String, charset:String, algorithm:String) -> String:
|
|
#
|
|
# var serv_type:String = "xmpp"
|
|
# #var HA1a:PackedStringArray = PackedStringArray([username ,":", realm , ":", password])
|
|
# var HA1a:PackedByteArray = set_PackedByteArray(username + ":" + realm + ":" + password)
|
|
#
|
|
# #var titi = username + ":" + realm + ":" + password
|
|
# #print("titi:", titi)
|
|
# #HA1a.push_back(username + ":" + realm + ":" + password)
|
|
# #var o1:PackedByteArray = set_PackedByteArray(username + ":" + realm + ":" + password)
|
|
# #print("o1:", o1.get_string_from_ascii())
|
|
# print("HA1a:", HA1a.hex_encode())
|
|
# var HA1b:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + cnonce)
|
|
# print("HA1b:", HA1b.hex_encode())
|
|
# var HA1c:PackedByteArray = hash_md5(set_PackedByteArray(HA1a.hex_encode()))
|
|
# var HA1d:PackedByteArray = hash_md5(set_PackedByteArray(HA1c.hex_encode() + HA1b.hex_encode()))
|
|
# var method:String = "AUTHENTICATE:"
|
|
# var digest_uri:String = serv_type + "/" + server_xmpp_name
|
|
# var HA2:PackedByteArray
|
|
# if qop == "auth":
|
|
# #var t:PackedByteArray = set_PackedByteArray(method + ":" + digest_uri)
|
|
# HA2 = hash_md5(set_PackedByteArray(method + ":" + digest_uri))
|
|
# else:
|
|
# # HA2 = MD5(method:digestURI:MD5(entityBody))
|
|
# var entityBody:String = ""
|
|
# var u:PackedByteArray = hash_md5(set_PackedByteArray(entityBody))
|
|
# #var t1:PackedByteArray = set_PackedByteArray(method + ":" + digest_uri + ":")
|
|
# var t:PackedByteArray = set_PackedByteArray(method + ":" + digest_uri + ":" + u.hex_encode())
|
|
# HA2 = hash_md5(t)
|
|
# #var i1:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":")
|
|
# var response:PackedByteArray = hash_md5(set_PackedByteArray(
|
|
# HA1d.hex_encode() +
|
|
# ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" +
|
|
# HA2.hex_encode()))
|
|
# var ifrealm:String = ""
|
|
# if realm != "":
|
|
# ifrealm = ",realm=" + realm
|
|
# var msg: PackedByteArray = set_PackedByteArray(
|
|
# "username=\"" + username + "\"" +
|
|
# ifrealm +
|
|
# ",nonce=\"" + nonce + "\"" +
|
|
# ",cnonce=\"" + cnonce + "\"" +
|
|
# ",nc=" + nc +
|
|
# ",qop=" + qop +
|
|
# ",digest-uri=\"" + digest_uri + "\"" +
|
|
# ",response=" + response.hex_encode() +
|
|
# ",charset=\"" + charset + "\""
|
|
# ) # + response
|
|
## charset=utf-8,username="chris",realm="elwood.innosoft.com",
|
|
## nonce="OA6MG9tEQGm2hh",nc=00000001,cnonce="OA6MHXh6VqTrRk",
|
|
## digest-uri="imap/elwood.innosoft.com",
|
|
## response=d388dad90d4bbd760a152321f2143af7,qop=auth
|
|
# print("msg:", msg.get_string_from_ascii())
|
|
## var msf2:PackedStringArray = PackedStringArray()
|
|
## var mystring:String = 'username="rob",realm="cataclysm.cx",nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth,digest-uri="xmpp/cataclysm.cx",response=d388dad90d4bbd760a152321f2143af7,charset=utf-8,authzid="rob@cataclysm.cx/myResource"'.strip_edges()
|
|
## msf2.push_back(mystring)
|
|
## var sol:String = "dXNlcm5hbWU9InJvYiIscmVhbG09ImNhdGFjbHlzbS5jeCIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjbm9uY2U9Ik9BNk1IWGg2VnFUclJrIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2NhdGFjbHlzbS5jeCIscmVzcG9uc2U9ZDM4OGRhZDkwZDRiYmQ3NjBhMTUyMzIxZjIxNDNhZjcsY2hhcnNldD11dGYtOCxhdXRoemlkPSJyb2JAY2F0YWNseXNtLmN4L215UmVzb3VyY2Ui"
|
|
## print("---", "".join(msf2))
|
|
## print("msf2:", Marshalls.raw_to_base64(msf2.to_byte_array()))
|
|
## print("msf2:", Marshalls.utf8_to_base64("".join(msf2)))
|
|
## print("msf2:", Marshalls.utf8_to_base64(mystring))
|
|
## if sol != Marshalls.raw_to_base64(msf2.to_byte_array()):
|
|
## print("********************************************** FAILED 1" )
|
|
## if sol != Marshalls.utf8_to_base64(mystring):
|
|
## print("********************************************** FAILED 2")
|
|
## if sol != Marshalls.utf8_to_base64("".join(msf2)):
|
|
## print("********************************************** FAILED 3")
|
|
# var auth_account:String = Marshalls.utf8_to_base64(msg.get_string_from_ascii())
|
|
var request_sasl:String = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \
|
|
" >" + auth_account + "</response>"
|
|
send_ssl_string(request_sasl)
|
|
auhtentification_step += 1
|
|
return true
|
|
return false
|
|
|
|
|
|
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
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
|
|
func analyze_stream(msg:String) -> void:
|
|
partial_stanza += msg
|
|
if partial_stanza.is_empty():
|
|
return
|
|
var complete_stanza = true
|
|
while complete_stanza == true:
|
|
var parser = XMLParser.new()
|
|
if partial_stanza.length() == 0:
|
|
return
|
|
parser.open_buffer( set_PackedByteArray(partial_stanza) )
|
|
|
|
var element_name = ""
|
|
var node_offset = 0
|
|
|
|
if parser.read() == OK and parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
|
element_name = parser.get_node_name()
|
|
if parser.is_empty():
|
|
node_offset = partial_stanza.length()
|
|
if parser.read() == OK:
|
|
node_offset = parser.get_node_offset()
|
|
|
|
while parser.read() == OK and node_offset == 0:
|
|
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
|
if parser.get_node_name() == element_name:
|
|
if parser.read() == OK:
|
|
node_offset = parser.get_node_offset()
|
|
else:
|
|
node_offset = partial_stanza.length()
|
|
|
|
if node_offset > 0:
|
|
# We have found a complete stanza.
|
|
var new_stanza:String = partial_stanza.left(node_offset)
|
|
if node_offset == partial_stanza.length():
|
|
partial_stanza = ""
|
|
else:
|
|
partial_stanza = partial_stanza.right(-node_offset)
|
|
#emit_signal("new_stanza", new_stanza)
|
|
print("new_stanza:", new_stanza)
|
|
else:
|
|
complete_stanza = false
|
|
|
|
|
|
func escape_html(msg:String) -> String:
|
|
var tmp = msg
|
|
tmp.replace("&", "&")
|
|
tmp.replace("<", "<")
|
|
tmp.replace(">", ">")
|
|
tmp.replace("'", "'")
|
|
tmp.replace("\"", """)
|
|
return tmp
|
|
|
|
|
|
func send_message_to_old(message, to):
|
|
if xmpp_state != XMPPState.AUTHENTICATED:
|
|
return
|
|
var msg:String = "<message to='" + escape_html(to) + "' type='chat'>\n" + \
|
|
" <body>" + escape_html(message) + "</body>\n" + \
|
|
"</message>"
|
|
send_ssl_string(msg)
|
|
|
|
|
|
func send_message_to_1(message, to):
|
|
if xmpp_state != XMPPState.AUTHENTICATED:
|
|
return
|
|
var msg:String = "<?xml version='1.0'?><stream:stream from='" + account_name + "' to='" + escape_html(to) + "' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>\n" + \
|
|
"<message to='" + escape_html(to) + "' > <body>" + escape_html(message) + "</body>\n" + \
|
|
"</message></stream:stream>"
|
|
send_ssl_string(msg)
|
|
|
|
|
|
func send_message_to(message, to):
|
|
if xmpp_state != XMPPState.AUTHENTICATED:
|
|
return
|
|
var msg:String = "<?xml version='1.0'?><stream:stream from='" + account_name + "' to='" + escape_html(to) + "' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>\n" + \
|
|
"<message > <body>" + escape_html(message) + "</body>\n" + \
|
|
"</message></stream:stream>"
|
|
send_ssl_string(msg)
|
|
|
|
|
|
func start_stream():
|
|
if xmpp_state != XMPPState.AUTHENTICATED:
|
|
return
|
|
var msg:String = "<?xml version='1.0'?>" + \
|
|
"<stream:stream" + \
|
|
" from='" + escape_html(account_name) + "'" + \
|
|
" to='" + escape_html(server_xmpp_name) + "'" + \
|
|
" version='1.0'" + \
|
|
" xml:lang='en'" + \
|
|
" xmlns='jabber:client'" + \
|
|
" xmlns:stream='http://etherx.jabber.org/streams'>\n" + \
|
|
"</stream:stream>"
|
|
send_ssl_string(msg)
|
|
xmpp_state = XMPPState.SENDED_START_STREAM
|