@@ -3392,105 +3392,156 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
33923392 return data ;
33933393}
33943394
3395+ /**
3396+ * @brief load_group Load a Group section from a save file
3397+ * @param g Group to load
3398+ * @param g_c Reference to all groupchats, need for utility functions
3399+ * @param data Start of the data to deserialze
3400+ * @param length Length of data
3401+ * @return 0 on error, number of consumed bytes otherwise
3402+ */
33953403non_null ()
3396- static State_Load_Status load_conferences ( Group_Chats * g_c , const uint8_t * data , uint32_t length )
3404+ static uint32_t load_group ( Group_c * g , const Group_Chats * g_c , const uint8_t * data , uint32_t length )
33973405{
33983406 const uint8_t * init_data = data ;
33993407
3400- while ( length >= ( uint32_t )( data - init_data ) + SAVED_CONF_SIZE_CONSTANT ) {
3401- const int groupnumber = create_group_chat ( g_c );
3408+ // Initialize to default values so we can unconditionally free in case of an error
3409+ setup_conference ( g );
34023410
3403- if (groupnumber == -1 ) {
3404- return STATE_LOAD_STATUS_ERROR ;
3411+ g -> type = * data ;
3412+ ++ data ;
3413+
3414+ memcpy (g -> id , data , GROUP_ID_LENGTH );
3415+ data += GROUP_ID_LENGTH ;
3416+
3417+ lendian_bytes_to_host32 (& g -> message_number , data );
3418+ data += sizeof (uint32_t );
3419+
3420+ lendian_bytes_to_host16 (& g -> lossy_message_number , data );
3421+ data += sizeof (uint16_t );
3422+
3423+ lendian_bytes_to_host16 (& g -> peer_number , data );
3424+ data += sizeof (uint16_t );
3425+
3426+ lendian_bytes_to_host32 (& g -> numfrozen , data );
3427+ data += sizeof (uint32_t );
3428+
3429+ if (g -> numfrozen > 0 ) {
3430+ g -> frozen = (Group_Peer * )calloc (g -> numfrozen , sizeof (Group_Peer ));
3431+
3432+ if (g -> frozen == nullptr ) {
3433+ // Memory allocation failure
3434+ return 0 ;
34053435 }
3436+ }
34063437
3407- Group_c * g = & g_c -> chats [ groupnumber ] ;
3438+ g -> title_len = * data ;
34083439
3409- g -> type = * data ;
3410- ++ data ;
3440+ if (g -> title_len > MAX_NAME_LENGTH ) {
3441+ return 0 ;
3442+ }
34113443
3412- memcpy (g -> id , data , GROUP_ID_LENGTH );
3413- data += GROUP_ID_LENGTH ;
3444+ ++ data ;
34143445
3415- lendian_bytes_to_host32 (& g -> message_number , data );
3416- data += sizeof (uint32_t );
3446+ assert ((data - init_data ) < UINT32_MAX );
34173447
3418- lendian_bytes_to_host16 (& g -> lossy_message_number , data );
3419- data += sizeof (uint16_t );
3448+ if (length < (uint32_t )(data - init_data ) + g -> title_len ) {
3449+ return 0 ;
3450+ }
34203451
3421- lendian_bytes_to_host16 ( & g -> peer_number , data );
3422- data += sizeof ( uint16_t ) ;
3452+ memcpy ( g -> title , data , g -> title_len );
3453+ data += g -> title_len ;
34233454
3424- lendian_bytes_to_host32 (& g -> numfrozen , data );
3425- data += sizeof (uint32_t );
3455+ for (uint32_t j = 0 ; j < g -> numfrozen ; ++ j ) {
34263456
3427- if (g -> numfrozen > 0 ) {
3428- g -> frozen = (Group_Peer * )calloc (g -> numfrozen , sizeof (Group_Peer ));
3457+ assert ((data - init_data ) < UINT32_MAX );
34293458
3430- if (g -> frozen == nullptr ) {
3431- return STATE_LOAD_STATUS_ERROR ;
3432- }
3459+ if (length < (uint32_t )(data - init_data ) + SAVED_PEER_SIZE_CONSTANT ) {
3460+ return 0 ;
34333461 }
34343462
3435- g -> title_len = * data ;
3463+ Group_Peer * peer = & g -> frozen [j ];
3464+ memset (peer , 0 , sizeof (Group_Peer ));
34363465
3437- if (g -> title_len > MAX_NAME_LENGTH ) {
3438- return STATE_LOAD_STATUS_ERROR ;
3466+ id_copy (peer -> real_pk , data );
3467+ data += CRYPTO_PUBLIC_KEY_SIZE ;
3468+ id_copy (peer -> temp_pk , data );
3469+ data += CRYPTO_PUBLIC_KEY_SIZE ;
3470+
3471+ lendian_bytes_to_host16 (& peer -> peer_number , data );
3472+ data += sizeof (uint16_t );
3473+
3474+ lendian_bytes_to_host64 (& peer -> last_active , data );
3475+ data += sizeof (uint64_t );
3476+
3477+ peer -> nick_len = * data ;
3478+
3479+ if (peer -> nick_len > MAX_NAME_LENGTH ) {
3480+ return 0 ;
34393481 }
34403482
34413483 ++ data ;
3484+ assert ((data - init_data ) < UINT32_MAX );
34423485
3443- if (length < (uint32_t )(data - init_data ) + g -> title_len ) {
3444- return STATE_LOAD_STATUS_ERROR ;
3486+ if (length < (uint32_t )(data - init_data ) + peer -> nick_len ) {
3487+ return 0 ;
34453488 }
34463489
3447- memcpy (g -> title , data , g -> title_len );
3448- data += g -> title_len ;
3490+ memcpy (peer -> nick , data , peer -> nick_len );
3491+ data += peer -> nick_len ;
34493492
3450- for (uint32_t j = 0 ; j < g -> numfrozen ; ++ j ) {
3451- if (length < (uint32_t )(data - init_data ) + SAVED_PEER_SIZE_CONSTANT ) {
3452- return STATE_LOAD_STATUS_ERROR ;
3453- }
3454-
3455- Group_Peer * peer = & g -> frozen [j ];
3456- memset (peer , 0 , sizeof (Group_Peer ));
3493+ // NOTE: this relies on friends being loaded before conferences.
3494+ peer -> is_friend = (getfriend_id (g_c -> m , peer -> real_pk ) != -1 );
3495+ }
34573496
3458- id_copy (peer -> real_pk , data );
3459- data += CRYPTO_PUBLIC_KEY_SIZE ;
3460- id_copy (peer -> temp_pk , data );
3461- data += CRYPTO_PUBLIC_KEY_SIZE ;
3497+ if (g -> numfrozen > g -> maxfrozen ) {
3498+ g -> maxfrozen = g -> numfrozen ;
3499+ }
34623500
3463- lendian_bytes_to_host16 (& peer -> peer_number , data );
3464- data += sizeof (uint16_t );
3501+ g -> status = GROUPCHAT_STATUS_CONNECTED ;
34653502
3466- lendian_bytes_to_host64 (& peer -> last_active , data );
3467- data += sizeof (uint64_t );
3503+ id_copy (g -> real_pk , nc_get_self_public_key (g_c -> m -> net_crypto ));
34683504
3469- peer -> nick_len = * data ;
3505+ assert (( data - init_data ) < UINT32_MAX ) ;
34703506
3471- if (peer -> nick_len > MAX_NAME_LENGTH ) {
3472- return STATE_LOAD_STATUS_ERROR ;
3473- }
3507+ return (uint32_t )(data - init_data );
3508+ }
34743509
3475- ++ data ;
3510+ non_null ()
3511+ static State_Load_Status load_conferences_helper (Group_Chats * g_c , const uint8_t * data , uint32_t length )
3512+ {
3513+ const uint8_t * init_data = data ;
34763514
3477- if (length < (uint32_t )(data - init_data ) + peer -> nick_len ) {
3478- return STATE_LOAD_STATUS_ERROR ;
3479- }
3515+ while (length >= (uint32_t )(data - init_data ) + SAVED_CONF_SIZE_CONSTANT ) {
3516+ const int groupnumber = create_group_chat (g_c );
34803517
3481- memcpy ( peer -> nick , data , peer -> nick_len );
3482- data += peer -> nick_len ;
3518+ // Helpful for testing
3519+ assert ( groupnumber != -1 ) ;
34833520
3484- // NOTE: this relies on friends being loaded before conferences.
3485- peer -> is_friend = (getfriend_id (g_c -> m , peer -> real_pk ) != -1 );
3521+ if (groupnumber == -1 ) {
3522+ // If this fails there's a serious problem, don't bother with cleanup
3523+ return STATE_LOAD_STATUS_ERROR ;
34863524 }
34873525
3488- if (g -> numfrozen > g -> maxfrozen ) {
3489- g -> maxfrozen = g -> numfrozen ;
3526+ Group_c * g = & g_c -> chats [groupnumber ];
3527+
3528+ const uint32_t consumed = load_group (g , g_c , data , length - (uint32_t )(data - init_data ));
3529+
3530+ if (consumed == 0 ) {
3531+ // remove partially loaded stuff, wipe_group_chat must be able to wipe a partially loaded group
3532+ const bool ret = wipe_group_chat (g_c , groupnumber );
3533+
3534+ // HACK: suppress unused variable warning
3535+ if (!ret ) {
3536+ // wipe_group_chat(...) must be able to wipe partially allocated groups
3537+ assert (ret == true);
3538+ }
3539+
3540+ return STATE_LOAD_STATUS_ERROR ;
34903541 }
34913542
3492- g -> status = GROUPCHAT_STATUS_CONNECTED ;
3493- memcpy ( g -> real_pk , nc_get_self_public_key ( g_c -> m -> net_crypto ), CRYPTO_PUBLIC_KEY_SIZE );
3543+ data += consumed ;
3544+
34943545 const int peer_index = addpeer (g_c , groupnumber , g -> real_pk , dht_get_self_public_key (g_c -> m -> dht ), g -> peer_number ,
34953546 nullptr , true, false);
34963547
@@ -3504,6 +3555,27 @@ static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data,
35043555 return STATE_LOAD_STATUS_CONTINUE ;
35053556}
35063557
3558+ non_null ()
3559+ static State_Load_Status load_conferences (Group_Chats * g_c , const uint8_t * data , uint32_t length )
3560+ {
3561+ const State_Load_Status res = load_conferences_helper (g_c , data , length );
3562+
3563+ if (res == STATE_LOAD_STATUS_CONTINUE ) {
3564+ return res ;
3565+ }
3566+
3567+ // Loading failed, cleanup all Group_c
3568+
3569+ // save locally, because wipe_group_chat(...) modifies it
3570+ const uint16_t num_groups = g_c -> num_chats ;
3571+
3572+ for (uint16_t i = 0 ; i < num_groups ; ++ i ) {
3573+ wipe_group_chat (g_c , i );
3574+ }
3575+
3576+ return res ;
3577+ }
3578+
35073579bool conferences_load_state_section (Group_Chats * g_c , const uint8_t * data , uint32_t length , uint16_t type ,
35083580 State_Load_Status * status )
35093581{
0 commit comments