@@ -897,7 +897,7 @@ static void _auth(xmpp_conn_t *conn)
897897 }
898898}
899899
900- static void _auth_success (xmpp_conn_t * conn )
900+ static void _stream_negotiation_success (xmpp_conn_t * conn )
901901{
902902 tls_clear_password_cache (conn );
903903 conn -> stream_negotiation_completed = 1 ;
@@ -962,6 +962,7 @@ static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
962962 /* send bind request */
963963 iq = xmpp_iq_new (conn -> ctx , "set" , "_xmpp_bind1" );
964964 if (!iq ) {
965+ xmpp_stanza_release (bind );
965966 disconnect_mem_error (conn );
966967 return 0 ;
967968 }
@@ -1102,7 +1103,7 @@ static int
11021103_handle_bind (xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata )
11031104{
11041105 const char * type ;
1105- xmpp_stanza_t * iq , * enable , * session , * binding , * jid_stanza ;
1106+ xmpp_stanza_t * iq , * session , * binding , * jid_stanza , * enable = NULL ;
11061107
11071108 UNUSED (userdata );
11081109
@@ -1125,6 +1126,23 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
11251126 }
11261127 }
11271128
1129+ /* send enable directly after the bind request */
1130+ if (conn -> sm_state -> sm_support && !conn -> sm_disable ) {
1131+ enable = xmpp_stanza_new (conn -> ctx );
1132+ if (!enable ) {
1133+ disconnect_mem_error (conn );
1134+ return 0 ;
1135+ }
1136+ xmpp_stanza_set_name (enable , "enable" );
1137+ xmpp_stanza_set_ns (enable , XMPP_NS_SM );
1138+ if (!conn -> sm_state -> dont_request_resume )
1139+ xmpp_stanza_set_attribute (enable , "resume" , "true" );
1140+ handler_add (conn , _handle_sm , XMPP_NS_SM , NULL , NULL , NULL );
1141+ send_stanza (conn , enable , XMPP_QUEUE_SM_STROPHE );
1142+ conn -> sm_state -> sm_sent_nr = 0 ;
1143+ conn -> sm_state -> sm_enabled = 1 ;
1144+ }
1145+
11281146 /* establish a session if required */
11291147 if (conn -> session_required ) {
11301148 /* setup response handlers */
@@ -1155,22 +1173,12 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
11551173 send_stanza (conn , iq , XMPP_QUEUE_STROPHE );
11561174 }
11571175
1158- if (conn -> sm_state -> sm_support && !conn -> sm_disable ) {
1159- enable = xmpp_stanza_new (conn -> ctx );
1160- if (!enable ) {
1161- disconnect_mem_error (conn );
1162- return 0 ;
1163- }
1164- xmpp_stanza_set_name (enable , "enable" );
1165- xmpp_stanza_set_ns (enable , XMPP_NS_SM );
1166- if (!conn -> sm_state -> dont_request_resume )
1167- xmpp_stanza_set_attribute (enable , "resume" , "true" );
1168- handler_add (conn , _handle_sm , XMPP_NS_SM , NULL , NULL , NULL );
1169- send_stanza (conn , enable , XMPP_QUEUE_SM_STROPHE );
1170- }
1171-
1172- if (!conn -> session_required ) {
1173- _auth_success (conn );
1176+ /* if there's no xmpp session required and we didn't try to enable
1177+ * stream-management, we're done here and the stream-negotiation was
1178+ * successful
1179+ */
1180+ if (!conn -> session_required && !enable ) {
1181+ _stream_negotiation_success (conn );
11741182 }
11751183 } else {
11761184 strophe_error (conn -> ctx , "xmpp" , "Server sent malformed bind reply." );
@@ -1207,7 +1215,7 @@ _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
12071215 } else if (type && strcmp (type , "result" ) == 0 ) {
12081216 strophe_debug (conn -> ctx , "xmpp" , "Session establishment successful." );
12091217
1210- _auth_success (conn );
1218+ _stream_negotiation_success (conn );
12111219 } else {
12121220 strophe_error (conn -> ctx , "xmpp" ,
12131221 "Server sent malformed session reply." );
@@ -1238,23 +1246,59 @@ static int _handle_missing_legacy(xmpp_conn_t *conn, void *userdata)
12381246 return 0 ;
12391247}
12401248
1249+ static int _get_h_attribute (xmpp_stanza_t * stanza , unsigned long * ul_h )
1250+ {
1251+ const char * h = xmpp_stanza_get_attribute (stanza , "h" );
1252+ if (!h || string_to_ul (h , ul_h )) {
1253+ strophe_error (
1254+ stanza -> ctx , "xmpp" ,
1255+ "SM error: failed parsing 'h', \"%s\" got converted to %llu." ,
1256+ STR_MAYBE_NULL (h ), * ul_h );
1257+ return -1 ;
1258+ }
1259+ return 0 ;
1260+ }
1261+
1262+ static void _sm_queue_cleanup (xmpp_conn_t * conn , unsigned long ul_h )
1263+ {
1264+ xmpp_send_queue_t * e ;
1265+ while ((e = peek_queue_front (& conn -> sm_state -> sm_queue ))) {
1266+ if (e -> sm_h > ul_h )
1267+ break ;
1268+ e = pop_queue_front (& conn -> sm_state -> sm_queue );
1269+ strophe_free (conn -> ctx , queue_element_free (conn -> ctx , e ));
1270+ }
1271+ }
1272+
1273+ static void _sm_queue_resend (xmpp_conn_t * conn )
1274+ {
1275+ xmpp_send_queue_t * e ;
1276+ while ((e = pop_queue_front (& conn -> sm_state -> sm_queue ))) {
1277+ /* Re-send what was already sent out and is still in the
1278+ * SM queue (i.e. it hasn't been ACK'ed by the server)
1279+ */
1280+ strophe_debug_verbose (2 , conn -> ctx , "conn" , "SM_Q_RESEND: %p, h=%lu" , e ,
1281+ e -> sm_h );
1282+ send_raw (conn , e -> data , e -> len , e -> owner , NULL );
1283+ strophe_free (conn -> ctx , queue_element_free (conn -> ctx , e ));
1284+ }
1285+ }
1286+
12411287static int _handle_sm (xmpp_conn_t * const conn ,
12421288 xmpp_stanza_t * const stanza ,
12431289 void * const userdata )
12441290{
1245- xmpp_stanza_t * failed_cause ;
1246- const char * name , * id , * previd , * resume , * h , * cause ;
1247- xmpp_send_queue_t * e ;
1291+ xmpp_stanza_t * failed_cause , * bind = NULL ;
1292+ const char * name , * id , * previd , * resume , * cause ;
12481293 unsigned long ul_h = 0 ;
12491294
12501295 UNUSED (userdata );
12511296
12521297 name = xmpp_stanza_get_name (stanza );
12531298 if (!name )
1254- goto LBL_ERR ;
1299+ goto err_sm ;
12551300
12561301 if (strcmp (name , "enabled" ) == 0 ) {
1257- conn -> sm_state -> sm_enabled = 1 ;
12581302 conn -> sm_state -> sm_handled_nr = 0 ;
12591303 resume = xmpp_stanza_get_attribute (stanza , "resume" );
12601304 if (resume && (strcasecmp (resume , "true" ) || strcmp (resume , "1" ))) {
@@ -1264,28 +1308,29 @@ static int _handle_sm(xmpp_conn_t *const conn,
12641308 "SM error: server said it can resume, but "
12651309 "didn't provide an ID." );
12661310 name = NULL ;
1267- goto LBL_ERR ;
1311+ goto err_sm ;
12681312 }
12691313 conn -> sm_state -> can_resume = 1 ;
12701314 conn -> sm_state -> id = strophe_strdup (conn -> ctx , id );
12711315 }
1316+ /* We maybe have stuff in the SM queue if we tried to resume, but the
1317+ * server doesn't remember all details of our session, but the `h` was
1318+ * still available.
1319+ */
1320+ _sm_queue_resend (conn );
1321+ _stream_negotiation_success (conn );
12721322 } else if (strcmp (name , "resumed" ) == 0 ) {
12731323 previd = xmpp_stanza_get_attribute (stanza , "previd" );
12741324 if (!previd || strcmp (previd , conn -> sm_state -> previd )) {
12751325 strophe_error (conn -> ctx , "xmpp" ,
12761326 "SM error: previd didn't match, ours is \"%s\"." ,
12771327 conn -> sm_state -> previd );
12781328 name = NULL ;
1279- goto LBL_ERR ;
1329+ goto err_sm ;
12801330 }
1281- h = xmpp_stanza_get_attribute (stanza , "h" );
1282- if (!h || string_to_ul (h , & ul_h )) {
1283- strophe_error (conn -> ctx , "xmpp" ,
1284- "SM error: failed parsing 'h', it got converted "
1285- "to %llu." ,
1286- ul_h );
1331+ if (_get_h_attribute (stanza , & ul_h )) {
12871332 name = NULL ;
1288- goto LBL_ERR ;
1333+ goto err_sm ;
12891334 }
12901335 conn -> sm_state -> sm_enabled = 1 ;
12911336 conn -> sm_state -> id = conn -> sm_state -> previd ;
@@ -1296,54 +1341,53 @@ static int _handle_sm(xmpp_conn_t *const conn,
12961341 conn -> sm_state -> sm_sent_nr = conn -> sm_state -> sm_queue .head -> sm_h ;
12971342 else
12981343 conn -> sm_state -> sm_sent_nr = ul_h ;
1299- while ((e = pop_queue_front (& conn -> sm_state -> sm_queue ))) {
1300- if (e -> sm_h >= ul_h ) {
1301- /* Re-send what was already sent out and is still in the
1302- * SM queue (i.e. it hasn't been ACK'ed by the server)
1303- */
1304- send_raw (conn , e -> data , e -> len , e -> owner , NULL );
1305- }
1306- strophe_free (conn -> ctx , queue_element_free (conn -> ctx , e ));
1307- }
1344+ _sm_queue_cleanup (conn , ul_h );
1345+ _sm_queue_resend (conn );
13081346 strophe_debug (conn -> ctx , "xmpp" , "Session resumed successfully." );
1309- _auth_success (conn );
1347+ _stream_negotiation_success (conn );
13101348 } else if (strcmp (name , "failed" ) == 0 ) {
13111349 name = NULL ;
1350+ conn -> sm_state -> sm_enabled = 0 ;
13121351
13131352 failed_cause =
13141353 xmpp_stanza_get_child_by_ns (stanza , XMPP_NS_STANZAS_IETF );
13151354 if (!failed_cause )
1316- goto LBL_ERR ;
1355+ goto err_sm ;
13171356
13181357 cause = xmpp_stanza_get_name (failed_cause );
13191358 if (!cause )
1320- goto LBL_ERR ;
1359+ goto err_sm ;
13211360
1322- if (!strcmp (cause , "item-not-found" ) ||
1323- !strcmp (cause , "feature-not-implemented" )) {
1361+ if (!strcmp (cause , "item-not-found" )) {
13241362 if (conn -> sm_state -> resume ) {
1325- conn -> sm_state -> resume = 0 ;
1326- conn -> sm_state -> can_resume = 0 ;
1327- /* remember that the server reports having support
1328- * for resumption, but actually it doesn't ...
1363+ /* It's no error if there's no `h` attribute included
1364+ * but if there is, it gives a hint at what the server
1365+ * already received.
13291366 */
1330- conn -> sm_state -> dont_request_resume =
1331- !strcmp (cause , "feature-not-implemented" );
1332- strophe_free (conn -> ctx , conn -> sm_state -> previd );
1333- conn -> sm_state -> previd = NULL ;
1334- strophe_free (conn -> ctx , conn -> sm_state -> bound_jid );
1335- conn -> sm_state -> bound_jid = NULL ;
1336- _do_bind (conn , conn -> sm_state -> bind );
1337- conn -> sm_state -> bind = NULL ;
1367+ if (!_get_h_attribute (stanza , & ul_h )) {
1368+ /* In cases there's no `h` included, drop all elements. */
1369+ ul_h = (unsigned long )-1 ;
1370+ }
1371+ _sm_queue_cleanup (conn , ul_h );
13381372 }
1373+ } else if (!strcmp (cause , "feature-not-implemented" )) {
1374+ conn -> sm_state -> resume = 0 ;
1375+ conn -> sm_state -> can_resume = 0 ;
1376+ /* remember that the server reports having support
1377+ * for resumption, but actually it doesn't ...
1378+ */
1379+ conn -> sm_state -> dont_request_resume = 1 ;
13391380 }
1340- conn -> sm_state -> sm_handled_nr = 0 ;
1381+ bind = conn -> sm_state -> bind ;
1382+ conn -> sm_state -> bind = NULL ;
1383+ reset_sm_state (conn -> sm_state );
1384+ _do_bind (conn , bind );
13411385 } else {
13421386 /* unknown stanza received */
13431387 name = NULL ;
13441388 }
13451389
1346- LBL_ERR :
1390+ err_sm :
13471391 if (!name ) {
13481392 char * err = "Couldn't convert stanza to text!" ;
13491393 char * buf ;
@@ -1362,7 +1406,8 @@ static int _handle_sm(xmpp_conn_t *const conn,
13621406 buf );
13631407 if (buf != err )
13641408 strophe_free (conn -> ctx , buf );
1365- conn -> sm_state -> sm_enabled = 0 ;
1409+ /* Don't disable for <failure> cases, they're no hard errors */
1410+ conn -> sm_state -> sm_enabled = bind != NULL ;
13661411 }
13671412 return 0 ;
13681413}
@@ -1395,7 +1440,7 @@ _handle_legacy(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
13951440 /* auth succeeded */
13961441 strophe_debug (conn -> ctx , "xmpp" , "Legacy auth succeeded." );
13971442
1398- _auth_success (conn );
1443+ _stream_negotiation_success (conn );
13991444 } else {
14001445 strophe_error (conn -> ctx , "xmpp" ,
14011446 "Server sent us a legacy authentication "
@@ -1585,7 +1630,7 @@ int _handle_component_hs_response(xmpp_conn_t *conn,
15851630 xmpp_disconnect (conn );
15861631 return XMPP_EINT ;
15871632 } else {
1588- _auth_success (conn );
1633+ _stream_negotiation_success (conn );
15891634 }
15901635
15911636 /* We don't need this handler anymore, return 0 so it can be deleted
@@ -1607,8 +1652,8 @@ int _handle_missing_handshake(xmpp_conn_t *conn, void *userdata)
16071652void auth_handle_open_raw (xmpp_conn_t * conn )
16081653{
16091654 handler_reset_timed (conn , 0 );
1610- /* user handlers are not called before authentication is completed. */
1611- _auth_success (conn );
1655+ /* user handlers are not called before stream negotiation has completed. */
1656+ _stream_negotiation_success (conn );
16121657}
16131658
16141659void auth_handle_open_stub (xmpp_conn_t * conn )
0 commit comments