6262
6363static void _auth (xmpp_conn_t * conn );
6464static void _auth_legacy (xmpp_conn_t * conn );
65+ static void _handle_open_compress (xmpp_conn_t * conn );
6566static void _handle_open_sasl (xmpp_conn_t * conn );
6667static void _handle_open_tls (xmpp_conn_t * conn );
6768
@@ -72,6 +73,9 @@ static int _handle_component_hs_response(xmpp_conn_t *conn,
7273
7374static int
7475_handle_features_sasl (xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata );
76+ static int _handle_features_compress (xmpp_conn_t * conn ,
77+ xmpp_stanza_t * stanza ,
78+ void * userdata );
7579static int
7680_handle_sasl_result (xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata );
7781static int _handle_digestmd5_challenge (xmpp_conn_t * conn ,
@@ -207,10 +211,61 @@ static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
207211 return 0 ;
208212}
209213
214+ typedef void (* text_handler )(xmpp_conn_t * conn , const char * text );
215+ static void _foreach_child (xmpp_conn_t * conn ,
216+ xmpp_stanza_t * parent ,
217+ const char * name ,
218+ text_handler hndl )
219+ {
220+ xmpp_stanza_t * children ;
221+ for (children = xmpp_stanza_get_children (parent ); children ;
222+ children = xmpp_stanza_get_next (children )) {
223+ const char * child_name = xmpp_stanza_get_name (children );
224+ if (child_name && strcmp (child_name , name ) == 0 ) {
225+ char * text = xmpp_stanza_get_text (children );
226+ if (text == NULL )
227+ continue ;
228+
229+ hndl (conn , text );
230+
231+ strophe_free (conn -> ctx , text );
232+ }
233+ }
234+ }
235+
236+ static void _handle_sasl_children (xmpp_conn_t * conn , const char * text )
237+ {
238+ if (strcasecmp (text , "PLAIN" ) == 0 ) {
239+ conn -> sasl_support |= SASL_MASK_PLAIN ;
240+ } else if (strcasecmp (text , "EXTERNAL" ) == 0 &&
241+ (conn -> tls_client_cert || conn -> tls_client_key )) {
242+ conn -> sasl_support |= SASL_MASK_EXTERNAL ;
243+ } else if (strcasecmp (text , "DIGEST-MD5" ) == 0 ) {
244+ conn -> sasl_support |= SASL_MASK_DIGESTMD5 ;
245+ } else if (strcasecmp (text , "ANONYMOUS" ) == 0 ) {
246+ conn -> sasl_support |= SASL_MASK_ANONYMOUS ;
247+ } else {
248+ size_t n ;
249+ for (n = 0 ; n < scram_algs_num ; ++ n ) {
250+ if (strcasecmp (text , scram_algs [n ]-> scram_name ) == 0 ) {
251+ conn -> sasl_support |= scram_algs [n ]-> mask ;
252+ break ;
253+ }
254+ }
255+ }
256+ }
257+
258+ static void _handle_compression_children (xmpp_conn_t * conn , const char * text )
259+ {
260+ if (strcasecmp (text , "zlib" ) == 0 ) {
261+ conn -> compression_supported = 1 ;
262+ }
263+ }
264+
210265static int
211266_handle_features (xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata )
212267{
213- xmpp_stanza_t * child , * mech ;
268+ xmpp_stanza_t * child , * children ;
214269 const char * ns ;
215270 char * text ;
216271
@@ -222,56 +277,33 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
222277 /* check for TLS */
223278 if (!conn -> secured ) {
224279 if (!conn -> tls_disabled ) {
225- child = xmpp_stanza_get_child_by_name (stanza , "starttls" );
226- if (child ) {
227- ns = xmpp_stanza_get_ns (child );
228- conn -> tls_support = ns != NULL && strcmp (ns , XMPP_NS_TLS ) == 0 ;
280+ if (xmpp_stanza_get_child_by_name_and_ns (stanza , "starttls" ,
281+ XMPP_NS_TLS )) {
282+ conn -> tls_support = 1 ;
229283 }
230284 } else {
231285 conn -> tls_support = 0 ;
232286 }
233287 }
234288
235289 /* check for SASL */
236- child = xmpp_stanza_get_child_by_name (stanza , "mechanisms" );
237- ns = child ? xmpp_stanza_get_ns (child ) : NULL ;
238- if (child && ns && strcmp (ns , XMPP_NS_SASL ) == 0 ) {
239- for (mech = xmpp_stanza_get_children (child ); mech ;
240- mech = xmpp_stanza_get_next (mech )) {
241- if (xmpp_stanza_get_name (mech ) &&
242- strcmp (xmpp_stanza_get_name (mech ), "mechanism" ) == 0 ) {
243- text = xmpp_stanza_get_text (mech );
244- if (text == NULL )
245- continue ;
246-
247- if (strcasecmp (text , "PLAIN" ) == 0 ) {
248- conn -> sasl_support |= SASL_MASK_PLAIN ;
249- } else if (strcasecmp (text , "EXTERNAL" ) == 0 &&
250- (conn -> tls_client_cert || conn -> tls_client_key )) {
251- conn -> sasl_support |= SASL_MASK_EXTERNAL ;
252- } else if (strcasecmp (text , "DIGEST-MD5" ) == 0 ) {
253- conn -> sasl_support |= SASL_MASK_DIGESTMD5 ;
254- } else if (strcasecmp (text , "ANONYMOUS" ) == 0 ) {
255- conn -> sasl_support |= SASL_MASK_ANONYMOUS ;
256- } else {
257- size_t n ;
258- for (n = 0 ; n < scram_algs_num ; ++ n ) {
259- if (strcasecmp (text , scram_algs [n ]-> scram_name ) == 0 ) {
260- conn -> sasl_support |= scram_algs [n ]-> mask ;
261- break ;
262- }
263- }
264- }
265-
266- strophe_free (conn -> ctx , text );
267- }
268- }
290+ child = xmpp_stanza_get_child_by_name_and_ns (stanza , "mechanisms" ,
291+ XMPP_NS_SASL );
292+ if (child ) {
293+ _foreach_child (conn , child , "mechanism" , _handle_sasl_children );
269294 }
270295
271296 /* Disable PLAIN when other secure mechanisms are supported */
272297 if (conn -> sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS ))
273298 conn -> sasl_support &= ~SASL_MASK_PLAIN ;
274299
300+ /* check for compression */
301+ child = xmpp_stanza_get_child_by_name_and_ns (stanza , "compression" ,
302+ XMPP_NS_COMPRESSION );
303+ if (conn -> compression_allowed && child ) {
304+ _foreach_child (conn , child , "method" , _handle_compression_children );
305+ }
306+
275307 _auth (conn );
276308
277309 return 0 ;
@@ -339,7 +371,9 @@ _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
339371 (char * )userdata );
340372
341373 /* reset parser */
342- conn_prepare_reset (conn , _handle_open_sasl );
374+ conn_prepare_reset (conn , conn -> compression_allowed
375+ ? _handle_open_compress
376+ : _handle_open_sasl );
343377
344378 /* send stream tag */
345379 conn_open_stream (conn );
@@ -950,6 +984,17 @@ static void _handle_open_sasl(xmpp_conn_t *conn)
950984 NULL );
951985}
952986
987+ /* called when stream:stream tag received after compression has been enabled */
988+ static void _handle_open_compress (xmpp_conn_t * conn )
989+ {
990+ strophe_debug (conn -> ctx , "xmpp" , "Reopened stream successfully." );
991+
992+ /* setup stream:features handlers */
993+ handler_add (conn , _handle_features_compress , XMPP_NS_STREAMS , "features" ,
994+ NULL , NULL );
995+ handler_add_timed (conn , _handle_missing_features , FEATURES_TIMEOUT , NULL );
996+ }
997+
953998static int _do_bind (xmpp_conn_t * conn , xmpp_stanza_t * bind )
954999{
9551000 xmpp_stanza_t * iq , * res , * text ;
@@ -1007,6 +1052,49 @@ static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
10071052 return 0 ;
10081053}
10091054
1055+ static int _handle_compress_result (xmpp_conn_t * const conn ,
1056+ xmpp_stanza_t * const stanza ,
1057+ void * const userdata )
1058+ {
1059+ const char * name = xmpp_stanza_get_name (stanza );
1060+
1061+ if (!name )
1062+ return 0 ;
1063+ if (strcmp (name , "compressed" ) == 0 ) {
1064+ /* Stream compression enabled, we need to restart the stream */
1065+ strophe_debug (conn -> ctx , "xmpp" , "Stream compression enabled" );
1066+
1067+ /* reset parser */
1068+ conn_prepare_reset (conn , _handle_open_sasl );
1069+
1070+ /* make compression effective */
1071+ conn -> compress = 1 ;
1072+
1073+ /* send stream tag */
1074+ conn_open_stream (conn );
1075+ }
1076+ return 0 ;
1077+ }
1078+
1079+ static int _handle_features_compress (xmpp_conn_t * conn ,
1080+ xmpp_stanza_t * stanza ,
1081+ void * userdata )
1082+ {
1083+ const char * compress = "<compress xmlns='" XMPP_NS_COMPRESSION
1084+ "'><method>zlib</method></compress>" ;
1085+
1086+ UNUSED (userdata );
1087+
1088+ /* remove missing features handler */
1089+ xmpp_timed_handler_delete (conn , _handle_missing_features );
1090+
1091+ send_raw (conn , compress , strlen (compress ), XMPP_QUEUE_STROPHE , NULL );
1092+ handler_add (conn , _handle_compress_result , XMPP_NS_COMPRESSION , NULL , NULL ,
1093+ NULL );
1094+
1095+ return 0 ;
1096+ }
1097+
10101098static int
10111099_handle_features_sasl (xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata )
10121100{
0 commit comments