2023-12-17 00:03:48 +00:00
extends Node
# Stream XMPP
# Author : AleaJactaEst
#
# https://xmpp.org/extensions/xep-0178.html
# https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml
2023-12-19 00:18:26 +00:00
# https://datatracker.ietf.org/doc/html/rfc6120#section-6.5
2023-12-17 00:03:48 +00:00
# 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 )
2023-12-24 00:17:01 +00:00
signal chat_connected ( )
2023-12-17 00:03:48 +00:00
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
2023-12-18 20:25:25 +00:00
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
2023-12-18 21:53:26 +00:00
SELECT_MECHANISM_AUTHENTICATE ,
2023-12-19 00:18:26 +00:00
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
2023-12-18 21:53:26 +00:00
AUTHENTICATE_FAILURE ,
AUTHENTICATE_SUCCESS ,
2023-12-18 20:25:25 +00:00
AUTHENTICATED , # We finished authenticate
2023-12-18 21:53:26 +00:00
NOT_AUTHORIZED , # Not Authorize
ACCOUNT_DISABLED , # Account Disable
2023-12-24 00:17:01 +00:00
SENDED_START_STREAM ,
2023-12-18 21:53:26 +00:00
}
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
2023-12-18 20:25:25 +00:00
}
@ export var server_xmpp_name : String = " localhost " :
set = set_server_xmpp_name , get = get_server_xmpp_name
2023-12-17 00:03:48 +00:00
2023-12-18 20:25:25 +00:00
func set_server_xmpp_name ( value : String ) :
server_xmpp_name = value
2023-12-17 00:03:48 +00:00
2023-12-18 20:25:25 +00:00
func get_server_xmpp_name ( ) - > String :
return server_xmpp_name
2023-12-17 00:03:48 +00:00
@ 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
2023-12-18 20:25:25 +00:00
const MAX_WAIT_CONNECTING : float = 60.0
const MAX_WAIT_MISSING_TLS : float = 1800.0
2023-12-18 21:53:26 +00:00
const MAX_WAIT_ACCOUNT_DISABLED : float = 900.0
const MAX_WAIT_NOT_AUTHORIZED : float = 18000.0
2023-12-18 20:25:25 +00:00
2023-12-17 00:03:48 +00:00
var try_connect : int = 0
2023-12-18 20:25:25 +00:00
var count_connecting_time : float = MAX_WAIT_CONNECTING
2023-12-17 00:03:48 +00:00
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
2023-12-18 20:25:25 +00:00
var xmpp_state = XMPPState . NONE
var authentication_methods = [ ]
2023-12-18 21:53:26 +00:00
var selected_mechanism_authenticate : String = " "
2023-12-24 00:17:01 +00:00
var order_preference_mechanism_authenticate : Array = [ ' PLAIN ' ]
#var order_preference_mechanism_authenticate: Array = ['DIGEST-MD5']
var banned_mechanism_authenticate : Array = [ ' DIGEST-MD5 ' ]
2023-12-19 00:18:26 +00:00
#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 = [ ]
2023-12-18 21:53:26 +00:00
2023-12-24 00:17:01 +00:00
const listcar : String = " AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn0123456789 "
2023-12-18 20:25:25 +00:00
2023-12-19 00:18:26 +00:00
#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
2023-12-17 00:03:48 +00:00
2023-12-18 20:25:25 +00:00
func _init ( ) - > void :
2023-12-17 00:03:48 +00:00
var language = OS . get_locale ( )
set_locale ( language )
tgt_peer = tcp_peer
2023-12-18 20:25:25 +00:00
# reinit_stream()
2023-12-24 00:17:01 +00:00
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 )
2023-12-18 20:25:25 +00:00
func _process ( delta ) - > void :
2023-12-19 00:18:26 +00:00
#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])
2023-12-18 20:25:25 +00:00
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
2023-12-18 21:53:26 +00:00
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
2023-12-18 20:25:25 +00:00
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
2023-12-18 21:53:26 +00:00
elif response . begins_with ( " failure " ) :
2023-12-18 20:25:25 +00:00
send_msg_error . emit ( tr ( " TlS negotiation failed. " ) )
count_connecting_time = 0
xmpp_state = XMPPState . MISSING_TLS
2023-12-18 21:53:26 +00:00
else :
send_msg_error . emit ( tr ( " TlS negotiation failed. (unknow return) " ) )
count_connecting_time = 0
xmpp_state = XMPPState . MISSING_TLS
2023-12-18 20:25:25 +00:00
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
2023-12-18 21:53:26 +00:00
xmpp_state = XMPPState . SELECT_MECHANISM_AUTHENTICATE
2023-12-18 20:25:25 +00:00
#send_msg_debug.emit("AUTHENTICATE_STEP_1")
# elif response.begins_with("<success"):
# stream_status = self.StreamState.STANZA
2023-12-18 21:53:26 +00:00
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
2023-12-19 00:18:26 +00:00
auhtentification_step = 0
elif selected_mechanism_authenticate == " DIGEST-MD5 " :
xmpp_state = XMPPState . AUTHENTICATE_STEP_DIGEST_MD5
auhtentification_step = 0
auhtentification_challenge = [ ]
2023-12-18 21:53:26 +00:00
if xmpp_state == XMPPState . AUTHENTICATE_STEP_PLAIN :
2023-12-19 00:18:26 +00:00
# https://datatracker.ietf.org/doc/html/rfc4616
2023-12-18 20:25:25 +00:00
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
2023-12-18 21:53:26 +00:00
negotiate_ssl_authenticate_plain ( )
2023-12-18 20:25:25 +00:00
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 )
2023-12-18 21:53:26 +00:00
var res : SASLReturn = get_result_authenticate ( response )
if res == SASLReturn . SUCCESS :
2023-12-18 20:25:25 +00:00
count_connecting_time = 0
xmpp_state = XMPPState . AUTHENTICATED
2023-12-18 21:53:26 +00:00
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 )
2023-12-19 00:18:26 +00:00
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 )
2023-12-24 00:17:01 +00:00
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()
2023-12-19 00:18:26 +00:00
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 " "
2023-12-18 21:53:26 +00:00
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
2023-12-18 20:25:25 +00:00
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 )
2023-12-24 00:17:01 +00:00
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 )
2023-12-18 20:25:25 +00:00
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 )
2023-12-18 21:53:26 +00:00
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 :
2023-12-19 00:18:26 +00:00
send_msg_debug . emit ( " Stream: sending request for PLAIN " )
2023-12-18 21:53:26 +00:00
var msg : PackedByteArray = PackedByteArray ( )
2023-12-19 00:18:26 +00:00
# [authzid]
#msg += server_xmpp_name.to_ascii_buffer()
# UTF8NUL
2023-12-18 21:53:26 +00:00
msg . push_back ( 0 )
2023-12-19 00:18:26 +00:00
# authcid
2023-12-24 00:17:01 +00:00
var t : String = account_name . split ( " @ " ) [ 0 ]
2023-12-18 21:53:26 +00:00
msg += t . to_ascii_buffer ( )
2023-12-19 00:18:26 +00:00
# UTF8NUL
2023-12-18 21:53:26 +00:00
msg . push_back ( 0 )
2023-12-19 00:18:26 +00:00
# passwd
2023-12-18 21:53:26 +00:00
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 )
2023-12-18 20:25:25 +00:00
2023-12-24 00:17:01 +00:00
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 )
2023-12-19 00:18:26 +00:00
func negotiate_ssl_authenticate_digest_md5 ( ) - > bool :
2023-12-24 00:17:01 +00:00
""" NE MARCHE PAS !!!!!! """
2023-12-19 00:18:26 +00:00
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 = " "
2023-12-24 00:17:01 +00:00
var cnonce : String = " "
var nc : String = " 00000001 "
var username : String = account_name . split ( " @ " ) [ 0 ]
#var username:String = account_name
2023-12-19 00:18:26 +00:00
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 )
2023-12-24 00:17:01 +00:00
if qop != " auth " and qop != " auth-int " :
2023-12-19 00:18:26 +00:00
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
2023-12-24 00:17:01 +00:00
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 )
2023-12-19 00:18:26 +00:00
auhtentification_step += 1
return true
return false
2023-12-18 20:25:25 +00:00
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
#
#
#
2023-12-19 00:18:26 +00:00
#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
2023-12-17 00:03:48 +00:00
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())
2023-12-18 20:25:25 +00:00
var res = tcp_peer . connect_to_host ( server_xmpp_name , port_number )
2023-12-17 00:03:48 +00:00
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 ( ) )
2023-12-18 20:25:25 +00:00
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 ( ) )
2023-12-17 00:03:48 +00:00
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)
2023-12-19 00:18:26 +00:00
#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)
2023-12-17 00:03:48 +00:00
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
2023-12-19 00:18:26 +00:00
#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()
2023-12-17 00:03:48 +00:00
2023-12-19 00:18:26 +00:00
#
#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
2023-12-24 00:17:01 +00:00
#
#
#
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