/work/toxcore/group_pack.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2020 The TokTok team. |
3 | | * Copyright © 2015 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Packer and unpacker functions for saving and loading groups. |
8 | | */ |
9 | | |
10 | | #include "group_pack.h" |
11 | | |
12 | | #include <stdint.h> |
13 | | #include <stdlib.h> |
14 | | #include <string.h> |
15 | | |
16 | | #include "DHT.h" |
17 | | #include "bin_pack.h" |
18 | | #include "bin_unpack.h" |
19 | | #include "ccompat.h" |
20 | | #include "group_common.h" |
21 | | #include "group_moderation.h" |
22 | | #include "logger.h" |
23 | | #include "util.h" |
24 | | |
25 | | bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out) |
26 | 2.87k | { |
27 | 2.87k | switch (value) { |
28 | 1.37k | case GI_PUBLIC: { |
29 | 1.37k | *out = GI_PUBLIC; |
30 | 1.37k | return true; |
31 | 0 | } |
32 | | |
33 | 246 | case GI_PRIVATE: { |
34 | 246 | *out = GI_PRIVATE; |
35 | 246 | return true; |
36 | 0 | } |
37 | | |
38 | 1.25k | default: { |
39 | 1.25k | *out = GI_PUBLIC; |
40 | 1.25k | return false; |
41 | 0 | } |
42 | 2.87k | } |
43 | 2.87k | } |
44 | | |
45 | | bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out) |
46 | 2.87k | { |
47 | 2.87k | switch (value) { |
48 | 1.25k | case GV_ALL: { |
49 | 1.25k | *out = GV_ALL; |
50 | 1.25k | return true; |
51 | 0 | } |
52 | | |
53 | 30 | case GV_MODS: { |
54 | 30 | *out = GV_MODS; |
55 | 30 | return true; |
56 | 0 | } |
57 | | |
58 | 5 | case GV_FOUNDER: { |
59 | 5 | *out = GV_FOUNDER; |
60 | 5 | return true; |
61 | 0 | } |
62 | | |
63 | 1.58k | default: { |
64 | 1.58k | *out = GV_ALL; |
65 | 1.58k | return false; |
66 | 0 | } |
67 | 2.87k | } |
68 | 2.87k | } |
69 | | |
70 | | non_null() |
71 | | static bool load_unpack_state_values(GC_Chat *chat, Bin_Unpack *bu) |
72 | 11.8k | { |
73 | 11.8k | if (!bin_unpack_array_fixed(bu, 8, nullptr)) { |
74 | 101 | LOGGER_ERROR(chat->log, "Group state values array malformed"); |
75 | 101 | return false; |
76 | 101 | } |
77 | | |
78 | 11.7k | bool manually_disconnected = false; |
79 | 11.7k | uint8_t privacy_state = 0; |
80 | 11.7k | uint8_t voice_state = 0; |
81 | | |
82 | 11.7k | if (!(bin_unpack_bool(bu, &manually_disconnected) |
83 | 11.7k | && bin_unpack_u16(bu, &chat->shared_state.group_name_len) |
84 | 11.7k | && bin_unpack_u08(bu, &privacy_state) |
85 | 11.7k | && bin_unpack_u16(bu, &chat->shared_state.maxpeers) |
86 | 11.7k | && bin_unpack_u16(bu, &chat->shared_state.password_length) |
87 | 11.7k | && bin_unpack_u32(bu, &chat->shared_state.version) |
88 | 11.7k | && bin_unpack_u32(bu, &chat->shared_state.topic_lock) |
89 | 11.7k | && bin_unpack_u08(bu, &voice_state))) { |
90 | 9.26k | LOGGER_ERROR(chat->log, "Failed to unpack state value"); |
91 | 9.26k | return false; |
92 | 9.26k | } |
93 | | |
94 | 2.49k | chat->connection_state = manually_disconnected ? CS_DISCONNECTED : CS_CONNECTING; |
95 | 2.49k | group_privacy_state_from_int(privacy_state, &chat->shared_state.privacy_state); |
96 | 2.49k | group_voice_state_from_int(voice_state, &chat->shared_state.voice_state); |
97 | | |
98 | | // we always load saved groups as private in case the group became private while we were offline. |
99 | | // this will have no detrimental effect if the group is public, as the correct privacy |
100 | | // state will be set via sync. |
101 | 2.49k | chat->join_type = HJ_PRIVATE; |
102 | | |
103 | 2.49k | return true; |
104 | 11.7k | } |
105 | | |
106 | | non_null() |
107 | | static bool load_unpack_state_bin(GC_Chat *chat, Bin_Unpack *bu) |
108 | 2.49k | { |
109 | 2.49k | if (!bin_unpack_array_fixed(bu, 5, nullptr)) { |
110 | 469 | LOGGER_ERROR(chat->log, "Group state binary array malformed"); |
111 | 469 | return false; |
112 | 469 | } |
113 | | |
114 | 2.02k | if (!bin_unpack_bin_fixed(bu, chat->shared_state_sig, SIGNATURE_SIZE)) { |
115 | 1.48k | LOGGER_ERROR(chat->log, "Failed to unpack shared state signature"); |
116 | 1.48k | return false; |
117 | 1.48k | } |
118 | | |
119 | 543 | if (!bin_unpack_bin_fixed(bu, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY_SIZE)) { |
120 | 19 | LOGGER_ERROR(chat->log, "Failed to unpack founder public key"); |
121 | 19 | return false; |
122 | 19 | } |
123 | | |
124 | 524 | if (!(bin_unpack_bin_max(bu, chat->shared_state.group_name, &chat->shared_state.group_name_len, sizeof(chat->shared_state.group_name)) |
125 | 524 | && bin_unpack_bin_max(bu, chat->shared_state.password, &chat->shared_state.password_length, sizeof(chat->shared_state.password)) |
126 | 524 | && bin_unpack_bin_fixed(bu, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE))) { |
127 | 77 | LOGGER_ERROR(chat->log, "Failed to unpack state binary data"); |
128 | 77 | return false; |
129 | 77 | } |
130 | | |
131 | 447 | return true; |
132 | 524 | } |
133 | | |
134 | | non_null() |
135 | | static bool load_unpack_topic_info(GC_Chat *chat, Bin_Unpack *bu) |
136 | 447 | { |
137 | 447 | if (!bin_unpack_array_fixed(bu, 6, nullptr)) { |
138 | 18 | LOGGER_ERROR(chat->log, "Group topic array malformed"); |
139 | 18 | return false; |
140 | 18 | } |
141 | | |
142 | 429 | if (!(bin_unpack_u32(bu, &chat->topic_info.version) |
143 | 429 | && bin_unpack_u16(bu, &chat->topic_info.length) |
144 | 429 | && bin_unpack_u16(bu, &chat->topic_info.checksum) |
145 | 429 | && bin_unpack_bin_max(bu, chat->topic_info.topic, &chat->topic_info.length, sizeof(chat->topic_info.topic)) |
146 | 429 | && bin_unpack_bin_fixed(bu, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE) |
147 | 429 | && bin_unpack_bin_fixed(bu, chat->topic_sig, SIGNATURE_SIZE))) { |
148 | 70 | LOGGER_ERROR(chat->log, "Failed to unpack topic info"); |
149 | 70 | return false; |
150 | 70 | } |
151 | | |
152 | 359 | return true; |
153 | 429 | } |
154 | | |
155 | | non_null() |
156 | | static bool load_unpack_mod_list(GC_Chat *chat, Bin_Unpack *bu) |
157 | 359 | { |
158 | 359 | uint32_t actual_size = 0; |
159 | 359 | if (!bin_unpack_array_fixed(bu, 2, &actual_size)) { |
160 | 12 | LOGGER_ERROR(chat->log, "Group mod list array malformed: %d != 2", actual_size); |
161 | 12 | return false; |
162 | 12 | } |
163 | | |
164 | 347 | if (!bin_unpack_u16(bu, &chat->moderation.num_mods)) { |
165 | 44 | LOGGER_ERROR(chat->log, "Failed to unpack mod list value"); |
166 | 44 | return false; |
167 | 44 | } |
168 | | |
169 | 303 | if (chat->moderation.num_mods == 0) { |
170 | 81 | bin_unpack_nil(bu); |
171 | 81 | return true; |
172 | 81 | } |
173 | | |
174 | 222 | if (chat->moderation.num_mods > MOD_MAX_NUM_MODERATORS) { |
175 | 18 | LOGGER_ERROR(chat->log, "moderation count %u exceeds maximum %u", chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS); |
176 | 18 | chat->moderation.num_mods = MOD_MAX_NUM_MODERATORS; |
177 | 18 | } |
178 | | |
179 | 222 | uint8_t *packed_mod_list = (uint8_t *)malloc(chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE); |
180 | | |
181 | 222 | if (packed_mod_list == nullptr) { |
182 | 0 | LOGGER_ERROR(chat->log, "Failed to allocate memory for packed mod list"); |
183 | 0 | return false; |
184 | 0 | } |
185 | | |
186 | 222 | const size_t packed_size = chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE; |
187 | | |
188 | 222 | if (!bin_unpack_bin_fixed(bu, packed_mod_list, packed_size)) { |
189 | 26 | LOGGER_ERROR(chat->log, "Failed to unpack mod list binary data"); |
190 | 26 | free(packed_mod_list); |
191 | 26 | return false; |
192 | 26 | } |
193 | | |
194 | 196 | if (mod_list_unpack(&chat->moderation, packed_mod_list, packed_size, chat->moderation.num_mods) == -1) { |
195 | 0 | LOGGER_ERROR(chat->log, "Failed to unpack mod list info"); |
196 | 0 | free(packed_mod_list); |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 196 | free(packed_mod_list); |
201 | | |
202 | 196 | return true; |
203 | 196 | } |
204 | | |
205 | | non_null() |
206 | | static bool load_unpack_keys(GC_Chat *chat, Bin_Unpack *bu) |
207 | 277 | { |
208 | 277 | if (!bin_unpack_array_fixed(bu, 4, nullptr)) { |
209 | 10 | LOGGER_ERROR(chat->log, "Group keys array malformed"); |
210 | 10 | return false; |
211 | 10 | } |
212 | | |
213 | 267 | if (!(bin_unpack_bin_fixed(bu, chat->chat_public_key, EXT_PUBLIC_KEY_SIZE) |
214 | 267 | && bin_unpack_bin_fixed(bu, chat->chat_secret_key, EXT_SECRET_KEY_SIZE) |
215 | 267 | && bin_unpack_bin_fixed(bu, chat->self_public_key, EXT_PUBLIC_KEY_SIZE) |
216 | 267 | && bin_unpack_bin_fixed(bu, chat->self_secret_key, EXT_SECRET_KEY_SIZE))) { |
217 | 61 | LOGGER_ERROR(chat->log, "Failed to unpack keys"); |
218 | 61 | return false; |
219 | 61 | } |
220 | | |
221 | 206 | return true; |
222 | 267 | } |
223 | | |
224 | | non_null() |
225 | | static bool load_unpack_self_info(GC_Chat *chat, Bin_Unpack *bu) |
226 | 206 | { |
227 | 206 | if (!bin_unpack_array_fixed(bu, 4, nullptr)) { |
228 | 12 | LOGGER_ERROR(chat->log, "Group self info array malformed"); |
229 | 12 | return false; |
230 | 12 | } |
231 | | |
232 | 194 | uint8_t self_nick[MAX_GC_NICK_SIZE]; |
233 | 194 | uint16_t self_nick_len = 0; |
234 | 194 | uint8_t self_role = GR_USER; |
235 | 194 | uint8_t self_status = GS_NONE; |
236 | | |
237 | 194 | if (!(bin_unpack_u16(bu, &self_nick_len) |
238 | 194 | && bin_unpack_u08(bu, &self_role) |
239 | 194 | && bin_unpack_u08(bu, &self_status))) { |
240 | 12 | LOGGER_ERROR(chat->log, "Failed to unpack self values"); |
241 | 12 | return false; |
242 | 12 | } |
243 | | |
244 | 182 | if (self_nick_len > MAX_GC_NICK_SIZE) { |
245 | 0 | LOGGER_ERROR(chat->log, "self_nick too big (%u bytes), truncating to %d", self_nick_len, MAX_GC_NICK_SIZE); |
246 | 0 | self_nick_len = MAX_GC_NICK_SIZE; |
247 | 0 | } |
248 | | |
249 | 182 | if (!bin_unpack_bin_fixed(bu, self_nick, self_nick_len)) { |
250 | 3 | LOGGER_ERROR(chat->log, "Failed to unpack self nick bytes"); |
251 | 3 | return false; |
252 | 3 | } |
253 | | |
254 | | // we have to add ourself before setting self info |
255 | 179 | if (peer_add(chat, nullptr, chat->self_public_key) != 0) { |
256 | 0 | LOGGER_ERROR(chat->log, "Failed to add self to peer list"); |
257 | 0 | return false; |
258 | 0 | } |
259 | | |
260 | 179 | if (chat->numpeers == 0) { |
261 | 0 | LOGGER_ERROR(chat->log, "Failed to unpack self: numpeers should be > 0"); |
262 | 0 | return false; |
263 | 0 | } |
264 | | |
265 | 179 | GC_Peer *self = &chat->group[0]; |
266 | | |
267 | 179 | memcpy(self->gconn.addr.public_key, chat->self_public_key, EXT_PUBLIC_KEY_SIZE); |
268 | 179 | memcpy(self->nick, self_nick, self_nick_len); |
269 | 179 | self->nick_length = self_nick_len; |
270 | 179 | self->role = (Group_Role)self_role; |
271 | 179 | self->status = (Group_Peer_Status)self_status; |
272 | 179 | self->gconn.confirmed = true; |
273 | | |
274 | 179 | return true; |
275 | 179 | } |
276 | | |
277 | | non_null() |
278 | | static bool load_unpack_saved_peers(GC_Chat *chat, Bin_Unpack *bu) |
279 | 179 | { |
280 | 179 | if (!bin_unpack_array_fixed(bu, 2, nullptr)) { |
281 | 22 | LOGGER_ERROR(chat->log, "Group saved peers array malformed"); |
282 | 22 | return false; |
283 | 22 | } |
284 | | |
285 | | // Saved peers |
286 | 157 | uint16_t saved_peers_size = 0; |
287 | | |
288 | 157 | if (!bin_unpack_u16(bu, &saved_peers_size)) { |
289 | 1 | LOGGER_ERROR(chat->log, "Failed to unpack saved peers value"); |
290 | 1 | return false; |
291 | 1 | } |
292 | | |
293 | 156 | if (saved_peers_size == 0) { |
294 | 13 | bin_unpack_nil(bu); |
295 | 13 | return true; |
296 | 13 | } |
297 | | |
298 | 143 | uint8_t *saved_peers = (uint8_t *)malloc(saved_peers_size * GC_SAVED_PEER_SIZE); |
299 | | |
300 | 143 | if (saved_peers == nullptr) { |
301 | 1 | LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peer list"); |
302 | 1 | return false; |
303 | 1 | } |
304 | | |
305 | 142 | if (!bin_unpack_bin_fixed(bu, saved_peers, saved_peers_size)) { |
306 | 17 | LOGGER_ERROR(chat->log, "Failed to unpack saved peers binary data"); |
307 | 17 | free(saved_peers); |
308 | 17 | return false; |
309 | 17 | } |
310 | | |
311 | 125 | if (unpack_gc_saved_peers(chat, saved_peers, saved_peers_size) == -1) { |
312 | 106 | LOGGER_ERROR(chat->log, "Failed to unpack saved peers"); // recoverable error |
313 | 106 | } |
314 | | |
315 | 125 | free(saved_peers); |
316 | | |
317 | 125 | return true; |
318 | 142 | } |
319 | | |
320 | | bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu) |
321 | 14.2k | { |
322 | 14.2k | uint32_t actual_size; |
323 | 14.2k | if (!bin_unpack_array_fixed(bu, 7, &actual_size)) { |
324 | 2.34k | LOGGER_ERROR(chat->log, "Group info array malformed: %d != 7", actual_size); |
325 | 2.34k | return false; |
326 | 2.34k | } |
327 | | |
328 | 11.8k | return load_unpack_state_values(chat, bu) |
329 | 11.8k | && load_unpack_state_bin(chat, bu) |
330 | 11.8k | && load_unpack_topic_info(chat, bu) |
331 | 11.8k | && load_unpack_mod_list(chat, bu) |
332 | 11.8k | && load_unpack_keys(chat, bu) |
333 | 11.8k | && load_unpack_self_info(chat, bu) |
334 | 11.8k | && load_unpack_saved_peers(chat, bu); |
335 | 14.2k | } |
336 | | |
337 | | non_null() |
338 | | static void save_pack_state_values(const GC_Chat *chat, Bin_Pack *bp) |
339 | 569 | { |
340 | 569 | bin_pack_array(bp, 8); |
341 | 569 | bin_pack_bool(bp, chat->connection_state == CS_DISCONNECTED); // 1 |
342 | 569 | bin_pack_u16(bp, chat->shared_state.group_name_len); // 2 |
343 | 569 | bin_pack_u08(bp, chat->shared_state.privacy_state); // 3 |
344 | 569 | bin_pack_u16(bp, chat->shared_state.maxpeers); // 4 |
345 | 569 | bin_pack_u16(bp, chat->shared_state.password_length); // 5 |
346 | 569 | bin_pack_u32(bp, chat->shared_state.version); // 6 |
347 | 569 | bin_pack_u32(bp, chat->shared_state.topic_lock); // 7 |
348 | 569 | bin_pack_u08(bp, chat->shared_state.voice_state); // 8 |
349 | 569 | } |
350 | | |
351 | | non_null() |
352 | | static void save_pack_state_bin(const GC_Chat *chat, Bin_Pack *bp) |
353 | 569 | { |
354 | 569 | bin_pack_array(bp, 5); |
355 | | |
356 | 569 | bin_pack_bin(bp, chat->shared_state_sig, SIGNATURE_SIZE); // 1 |
357 | 569 | bin_pack_bin(bp, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY_SIZE); // 2 |
358 | 569 | bin_pack_bin(bp, chat->shared_state.group_name, chat->shared_state.group_name_len); // 3 |
359 | 569 | bin_pack_bin(bp, chat->shared_state.password, chat->shared_state.password_length); // 4 |
360 | 569 | bin_pack_bin(bp, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE); // 5 |
361 | 569 | } |
362 | | |
363 | | non_null() |
364 | | static void save_pack_topic_info(const GC_Chat *chat, Bin_Pack *bp) |
365 | 569 | { |
366 | 569 | bin_pack_array(bp, 6); |
367 | | |
368 | 569 | bin_pack_u32(bp, chat->topic_info.version); // 1 |
369 | 569 | bin_pack_u16(bp, chat->topic_info.length); // 2 |
370 | 569 | bin_pack_u16(bp, chat->topic_info.checksum); // 3 |
371 | 569 | bin_pack_bin(bp, chat->topic_info.topic, chat->topic_info.length); // 4 |
372 | 569 | bin_pack_bin(bp, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE); // 5 |
373 | 569 | bin_pack_bin(bp, chat->topic_sig, SIGNATURE_SIZE); // 6 |
374 | 569 | } |
375 | | |
376 | | non_null() |
377 | | static void save_pack_mod_list(const GC_Chat *chat, Bin_Pack *bp) |
378 | 569 | { |
379 | 569 | bin_pack_array(bp, 2); |
380 | | |
381 | 569 | const uint16_t num_mods = min_u16(chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS); |
382 | | |
383 | 569 | if (num_mods == 0) { |
384 | 165 | bin_pack_u16(bp, num_mods); // 1 |
385 | 165 | bin_pack_nil(bp); // 2 |
386 | 165 | return; |
387 | 165 | } |
388 | | |
389 | 404 | uint8_t *packed_mod_list = (uint8_t *)malloc(num_mods * MOD_LIST_ENTRY_SIZE); |
390 | | |
391 | | // we can still recover without the mod list |
392 | 404 | if (packed_mod_list == nullptr) { |
393 | 0 | bin_pack_u16(bp, 0); // 1 |
394 | 0 | bin_pack_nil(bp); // 2 |
395 | 0 | LOGGER_ERROR(chat->log, "Failed to allocate memory for moderation list"); |
396 | 0 | return; |
397 | 0 | } |
398 | | |
399 | 404 | bin_pack_u16(bp, num_mods); // 1 |
400 | | |
401 | 404 | mod_list_pack(&chat->moderation, packed_mod_list); |
402 | | |
403 | 404 | const size_t packed_size = num_mods * MOD_LIST_ENTRY_SIZE; |
404 | | |
405 | 404 | bin_pack_bin(bp, packed_mod_list, packed_size); // 2 |
406 | | |
407 | 404 | free(packed_mod_list); |
408 | 404 | } |
409 | | |
410 | | non_null() |
411 | | static void save_pack_keys(const GC_Chat *chat, Bin_Pack *bp) |
412 | 569 | { |
413 | 569 | bin_pack_array(bp, 4); |
414 | | |
415 | 569 | bin_pack_bin(bp, chat->chat_public_key, EXT_PUBLIC_KEY_SIZE); // 1 |
416 | 569 | bin_pack_bin(bp, chat->chat_secret_key, EXT_SECRET_KEY_SIZE); // 2 |
417 | 569 | bin_pack_bin(bp, chat->self_public_key, EXT_PUBLIC_KEY_SIZE); // 3 |
418 | 569 | bin_pack_bin(bp, chat->self_secret_key, EXT_SECRET_KEY_SIZE); // 4 |
419 | 569 | } |
420 | | |
421 | | non_null() |
422 | | static void save_pack_self_info(const GC_Chat *chat, Bin_Pack *bp) |
423 | 569 | { |
424 | 569 | bin_pack_array(bp, 4); |
425 | | |
426 | 569 | GC_Peer *self = &chat->group[0]; |
427 | | |
428 | 569 | if (self->nick_length > MAX_GC_NICK_SIZE) { |
429 | 0 | LOGGER_ERROR(chat->log, "self_nick is too big (%u). Truncating to %d", self->nick_length, MAX_GC_NICK_SIZE); |
430 | 0 | self->nick_length = MAX_GC_NICK_SIZE; |
431 | 0 | } |
432 | | |
433 | 569 | bin_pack_u16(bp, self->nick_length); // 1 |
434 | 569 | bin_pack_u08(bp, (uint8_t)self->role); // 2 |
435 | 569 | bin_pack_u08(bp, self->status); // 3 |
436 | 569 | bin_pack_bin(bp, self->nick, self->nick_length); // 4 |
437 | 569 | } |
438 | | |
439 | | non_null() |
440 | | static void save_pack_saved_peers(const GC_Chat *chat, Bin_Pack *bp) |
441 | 569 | { |
442 | 569 | bin_pack_array(bp, 2); |
443 | | |
444 | 569 | uint8_t *saved_peers = (uint8_t *)malloc(GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE); |
445 | | |
446 | | // we can still recover without the saved peers list |
447 | 569 | if (saved_peers == nullptr) { |
448 | 4 | bin_pack_u16(bp, 0); // 1 |
449 | 4 | bin_pack_nil(bp); // 2 |
450 | 4 | LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peers list"); |
451 | 4 | return; |
452 | 4 | } |
453 | | |
454 | 565 | uint16_t packed_size = 0; |
455 | 565 | const int count = pack_gc_saved_peers(chat, saved_peers, GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE, &packed_size); |
456 | | |
457 | 565 | if (count < 0) { |
458 | 0 | LOGGER_ERROR(chat->log, "Failed to pack saved peers"); |
459 | 0 | } |
460 | | |
461 | 565 | bin_pack_u16(bp, packed_size); // 1 |
462 | | |
463 | 565 | if (packed_size == 0) { |
464 | 124 | bin_pack_nil(bp); // 2 |
465 | 124 | free(saved_peers); |
466 | 124 | return; |
467 | 124 | } |
468 | | |
469 | 441 | bin_pack_bin(bp, saved_peers, packed_size); // 2 |
470 | | |
471 | 441 | free(saved_peers); |
472 | 441 | } |
473 | | |
474 | | void gc_save_pack_group(const GC_Chat *chat, Bin_Pack *bp) |
475 | 2.64k | { |
476 | 2.64k | if (chat->numpeers == 0) { |
477 | 2.08k | LOGGER_ERROR(chat->log, "Failed to pack group: numpeers is 0"); |
478 | 2.08k | return; |
479 | 2.08k | } |
480 | | |
481 | 569 | bin_pack_array(bp, 7); |
482 | | |
483 | 569 | save_pack_state_values(chat, bp); // 1 |
484 | 569 | save_pack_state_bin(chat, bp); // 2 |
485 | 569 | save_pack_topic_info(chat, bp); // 3 |
486 | 569 | save_pack_mod_list(chat, bp); // 4 |
487 | 569 | save_pack_keys(chat, bp); // 5 |
488 | 569 | save_pack_self_info(chat, bp); // 6 |
489 | 569 | save_pack_saved_peers(chat, bp); // 7 |
490 | 569 | } |