diff options
Diffstat (limited to 'protocols/Telegram/tgl/binlog.c')
-rw-r--r-- | protocols/Telegram/tgl/binlog.c | 1829 |
1 files changed, 1829 insertions, 0 deletions
diff --git a/protocols/Telegram/tgl/binlog.c b/protocols/Telegram/tgl/binlog.c new file mode 100644 index 0000000000..ed5d514abf --- /dev/null +++ b/protocols/Telegram/tgl/binlog.c @@ -0,0 +1,1829 @@ +/* + This file is part of tgl-library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Copyright Vitaly Valtman 2013-2015 +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(WIN32) || defined(_WIN32) +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#include <io.h> +#include <sys/locking.h> +#else +#include <unistd.h> +#include <sys/file.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <openssl/bn.h> + +#include "tgl-binlog.h" +#include "mtproto-common.h" +//#include "net.h" +#include "mtproto-client.h" +#include "mtproto-utils.h" + +#include "tgl.h" +#include "auto.h" +#include "auto/auto-types.h" +#include "auto/auto-skip.h" +#include "auto/auto-store-ds.h" +#include "auto/auto-fetch-ds.h" +#include "auto/auto-free-ds.h" + +#include "tgl-structures.h" +#include "tgl-methods-in.h" + +#include <openssl/sha.h> + +#define BINLOG_BUFFER_SIZE (1 << 20) +static int binlog_buffer[BINLOG_BUFFER_SIZE]; +static int *rptr; +static int *wptr; +//static int TLS->binlog_fd; +static int in_replay_log; // should be used ONLY for DEBUG + + +#define MAX_LOG_EVENT_SIZE (1 << 17) + +char *get_binlog_file_name (void); + +static void *alloc_log_event (int l) { + return binlog_buffer; +} + +static int mystreq1 (const char *a, const char *b, int l) { + if ((int)strlen (a) != l) { return 1; } + return memcmp (a, b, l); +} + +static long long binlog_pos; + +static int fetch_comb_binlog_start (struct tgl_state *TLS, void *extra) { + return 0; +} + +/* {{{ DC option */ +static int fetch_comb_binlog_dc_option (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + vlogprintf (E_NOTICE, "DC%d '%.*s' update: %.*s:%d\n", + DS_LVAL (DS_U->dc), + DS_RSTR (DS_U->name), + DS_RSTR (DS_U->ip), + DS_LVAL (DS_U->port) + ); + + tglmp_alloc_dc (TLS, + 0, + DS_LVAL (DS_U->dc), + DS_STR_DUP (DS_U->ip), + DS_LVAL (DS_U->port) + ); + return 0; +} + +static int fetch_comb_binlog_dc_option_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + vlogprintf (E_NOTICE, "DC%d '%.*s' update: %.*s:%d\n", + DS_LVAL (DS_U->dc), + DS_RSTR (DS_U->name), + DS_RSTR (DS_U->ip), + DS_LVAL (DS_U->port) + ); + + tglmp_alloc_dc (TLS, + DS_LVAL (DS_U->flags), + DS_LVAL (DS_U->dc), + DS_STR_DUP (DS_U->ip), + DS_LVAL (DS_U->port) + ); + return 0; +} +/* }}} */ + +/* {{{ Auth key */ +static int fetch_comb_binlog_auth_key (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + int num = DS_LVAL (DS_U->dc); + assert (num > 0 && num <= MAX_DC_ID); + assert (TLS->DC_list[num]); + + tglf_fetch_int_tuple ((void *)TLS->DC_list[num]->auth_key, DS_U->key->key, 64); + + static unsigned char sha1_buffer[20]; + SHA1 ((void *)TLS->DC_list[num]->auth_key, 256, sha1_buffer); + TLS->DC_list[num]->auth_key_id = *(long long *)(sha1_buffer + 12); + + TLS->DC_list[num]->flags |= TGLDCF_AUTHORIZED; + return 0; +} +/* }}} */ + +/* {{{ Default dc */ +static int fetch_comb_binlog_default_dc (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + int num = DS_LVAL (DS_U->dc); + assert (num > 0 && num <= MAX_DC_ID); + TLS->DC_working = TLS->DC_list[num]; + TLS->dc_working_num = num; + return 0; +} +/* }}} */ + +/* {{{ DC signed */ +static int fetch_comb_binlog_dc_signed (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + int num = DS_LVAL (DS_U->dc); + assert (num > 0 && num <= MAX_DC_ID); + assert (TLS->DC_list[num]); + TLS->DC_list[num]->flags |= TGLDCF_LOGGED_IN; + return 0; +} +/* }}} */ + +/* {{{ our user_id */ +static int fetch_comb_binlog_our_id (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + TLS->our_id = DS_LVAL (DS_U->id); + assert (TLS->our_id > 0); + if (TLS->callback.our_id) { + TLS->callback.our_id (TLS, TLS->our_id); + } + return 0; +} +/* }}} */ + +/* {{{ Set DH params */ +static int fetch_comb_binlog_set_dh_params (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + if (TLS->encr_prime) { tfree (TLS->encr_prime, 256); BN_free (TLS->encr_prime_bn); } + + TLS->encr_root = DS_LVAL (DS_U->root); + TLS->encr_prime = talloc (256); + tglf_fetch_int_tuple ((void *)TLS->encr_prime, DS_U->prime->key, 64); + + TLS->encr_prime_bn = BN_new (); + BN_bin2bn ((void *)TLS->encr_prime, 256, TLS->encr_prime_bn); + TLS->encr_param_version = DS_LVAL (DS_U->version); + + assert (tglmp_check_DH_params (TLS, TLS->encr_prime_bn, TLS->encr_root) >= 0); + + return 0; +} +/* }}} */ + +/* {{{ Set pts, qts, date, seq */ +static int fetch_comb_binlog_set_pts (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + TLS->pts = DS_LVAL (DS_U->pts); + return 0; +} + +static int fetch_comb_binlog_set_qts (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + TLS->qts = DS_LVAL (DS_U->qts); + return 0; +} + +static int fetch_comb_binlog_set_date (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + TLS->date = DS_LVAL (DS_U->date); + return 0; +} + +static int fetch_comb_binlog_set_seq (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + TLS->seq = DS_LVAL (DS_U->seq); + return 0; +} +/* }}} */ + +/* {{{ delete user */ +static int fetch_comb_binlog_user_delete (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_USER (DS_LVAL (DS_U->id)); + tgl_peer_t *U = tgl_peer_get (TLS, id); + assert (U); + U->flags |= TGLUF_DELETED; + + if (TLS->callback.user_update) { + TLS->callback.user_update (TLS, (void *)U, TGL_UPDATE_DELETED); + } + return 0; +} +/* }}} */ + +/* {{{ delete secret chat */ +static int fetch_comb_binlog_encr_chat_delete (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_U->id)); + tgl_peer_t *_U = tgl_peer_get (TLS, id); + assert (_U); + struct tgl_secret_chat *U = &_U->encr_chat; + memset (U->key, 0, sizeof (U->key)); + U->flags |= TGLPF_DELETED; + U->state = sc_deleted; + if (U->g_key) { + tfree_secure (U->g_key, 256); + U->g_key = 0; + } + + if (TLS->callback.secret_chat_update) { + TLS->callback.secret_chat_update (TLS, U, TGL_UPDATE_DELETED); + } + return 0; +} +/* }}} */ + +static int fetch_comb_binlog_user_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_USER (DS_LVAL (DS_U->id)); + tgl_peer_t *_U = tgl_peer_get (TLS, id); + + int flags = DS_LVAL (DS_U->flags); + + unsigned updates = 0; + + if (flags & TGLPF_CREATE) { + if (!_U) { + _U = talloc0 (sizeof (*_U)); + _U->id = id; + tglp_insert_encrypted_chat (TLS, _U); + } else { + assert (!(_U->flags & TGLPF_CREATED)); + } + updates |= TGL_UPDATE_CREATED; + } else { + assert (_U->flags & TGLPF_CREATED); + } + + struct tgl_user *U = (void *)_U; + + if ((flags & 0xff) != (U->flags & 0xff)) { + updates |= TGL_UPDATE_FLAGS; + } + U->flags = flags & 0xffff; + + if (DS_U->access_hash) { + U->access_hash = DS_LVAL (DS_U->access_hash); + updates |= TGL_UPDATE_ACCESS_HASH; + } + + if (DS_U->first_name) { + if (U->first_name) { + tfree_str (U->first_name); + } + U->first_name = DS_STR_DUP (DS_U->first_name); + if (U->last_name) { + tfree_str (U->last_name); + } + U->last_name = DS_STR_DUP (DS_U->last_name); + + updates |= TGL_UPDATE_NAME; + + if (U->print_name) { + tglp_peer_delete_name (TLS, (void *)U); + tfree_str (U->print_name); + } + U->print_name = TLS->callback.create_print_name (TLS, U->id, U->first_name, U->last_name, 0, 0); + tglp_peer_insert_name (TLS, (void *)U); + } + + if (DS_U->phone) { + if (U->phone) { + tfree_str (U->phone); + } + U->phone = DS_STR_DUP (DS_U->phone); + updates |= TGL_UPDATE_PHONE; + } + + if (DS_U->username) { + if (U->username) { + tfree_str (U->username); + } + U->username = DS_STR_DUP (DS_U->username); + updates |= TGL_UPDATE_USERNAME; + } + + if (DS_U->photo) { + if (U->photo) { + tgls_free_photo (TLS, U->photo); + } + U->photo = tglf_fetch_alloc_photo_new (TLS, DS_U->photo); + U->flags |= TGLUF_HAS_PHOTO; + //updates |= TGL_UPDATE_PHOTO; + } + + if (DS_U->user_photo) { + U->photo_id = DS_LVAL (DS_U->user_photo->photo_id); + tglf_fetch_file_location_new (TLS, &U->photo_big, DS_U->user_photo->photo_big); + tglf_fetch_file_location_new (TLS, &U->photo_small, DS_U->user_photo->photo_small); + updates |= TGL_UPDATE_PHOTO; + } + + if (DS_U->real_first_name) { + if (U->real_first_name) { + tfree_str (U->real_first_name); + } + U->real_first_name = DS_STR_DUP (DS_U->real_first_name); + if (U->real_last_name) { + tfree_str (U->real_last_name); + } + U->real_last_name = DS_STR_DUP (DS_U->real_last_name); + + updates |= TGL_UPDATE_REAL_NAME; + } + + if (DS_U->last_read_in) { + U->last_read_in = DS_LVAL (DS_U->last_read_in); + tgls_messages_mark_read (TLS, U->last, 0, U->last_read_in); + } + + if (DS_U->last_read_out) { + U->last_read_out = DS_LVAL (DS_U->last_read_out); + tgls_messages_mark_read (TLS, U->last, TGLMF_OUT, U->last_read_out); + } + + if (DS_U->bot_info) { + if (U->bot_info) { + tgls_free_bot_info (TLS, U->bot_info); + } + U->bot_info = tglf_fetch_alloc_bot_info (TLS, DS_U->bot_info); + } + + if (TLS->callback.user_update && updates) { + TLS->callback.user_update (TLS, U, updates); + } + + return 0; +} + +static int fetch_comb_binlog_encr_chat_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_U->id)); + tgl_peer_t *_U = tgl_peer_get (TLS, id); + + int flags = DS_LVAL (DS_U->flags); + + unsigned updates = 0; + + if (flags & TGLPF_CREATE) { + if (!_U) { + _U = talloc0 (sizeof (*_U)); + _U->id = id; + tglp_insert_encrypted_chat (TLS, _U); + } else { + assert (!(_U->flags & TGLPF_CREATED)); + } + updates |= TGL_UPDATE_CREATED; + } else { + assert (_U->flags & TGLPF_CREATED); + } + + struct tgl_secret_chat *U = (void *)_U; + + if ((flags & 0xff) != (U->flags & 0xff)) { + updates |= TGL_UPDATE_FLAGS; + } + U->flags = flags & 0xffff; + + if (DS_U->access_hash) { + U->access_hash = DS_LVAL (DS_U->access_hash); + updates |= TGL_UPDATE_ACCESS_HASH; + } + + if (DS_U->date) { + U->date = DS_LVAL (DS_U->date); + } + + if (DS_U->admin) { + U->admin_id = DS_LVAL (DS_U->admin); + } + + if (DS_U->user_id) { + U->user_id = DS_LVAL (DS_U->user_id); + } + + if (DS_U->key_fingerprint) { + U->key_fingerprint = DS_LVAL (DS_U->key_fingerprint); + } + + if (DS_U->in_seq_no) { + U->in_seq_no = DS_LVAL (DS_U->in_seq_no); + U->out_seq_no = DS_LVAL (DS_U->out_seq_no); + U->last_in_seq_no = DS_LVAL (DS_U->last_in_seq_no); + } + + tgl_peer_t *Us = tgl_peer_get (TLS, TGL_MK_USER (U->user_id)); + + if (!U->print_name) { + if (Us) { + U->print_name = TLS->callback.create_print_name (TLS, id, "!", Us->user.first_name, Us->user.last_name, 0); + } else { + static char buf[100]; + tsnprintf (buf, 99, "user#%d", U->user_id); + U->print_name = TLS->callback.create_print_name (TLS, id, "!", buf, 0, 0); + } + tglp_peer_insert_name (TLS, (void *)U); + } + + if (DS_U->g_key) { + if (!U->g_key) { + U->g_key = talloc (256); + } + tglf_fetch_int_tuple ((void *)U->g_key, DS_U->g_key->key, 64); + } + + if (DS_U->key) { + tglf_fetch_int_tuple (U->key, DS_U->key->key, 64); + } + + if (DS_U->state) { + if (U->state == sc_waiting && DS_LVAL (DS_U->state) == sc_ok) { + tgl_do_create_keys_end (TLS, U); + } + if ((int)U->state != DS_LVAL (DS_U->state)) { + switch (DS_LVAL (DS_U->state)) { + case sc_request: + updates |= TGL_UPDATE_REQUESTED; + break; + case sc_ok: + updates |= TGL_UPDATE_WORKING; + vlogprintf (E_WARNING, "Secret chat in ok state\n"); + break; + default: + break; + } + } + U->state = DS_LVAL (DS_U->state); + } + + if (TLS->callback.secret_chat_update && updates) { + TLS->callback.secret_chat_update (TLS, U, updates); + } + + return 0; +} + +static int fetch_comb_binlog_chat_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_CHAT (DS_LVAL (DS_U->id)); + tgl_peer_t *_U = tgl_peer_get (TLS, id); + + int flags = DS_LVAL (DS_U->flags); + + unsigned updates = 0; + + if (flags & (1 << 16)) { + if (!_U) { + _U = talloc0 (sizeof (*_U)); + _U->id = id; + tglp_insert_chat (TLS, _U); + } else { + assert (!(_U->flags & TGLPF_CREATED)); + } + updates |= TGL_UPDATE_CREATED; + } else { + assert (_U->flags & TGLPF_CREATED); + } + + struct tgl_chat *C = &_U->chat; + + if ((flags & 0xff) != (C->flags & 0xff)) { + updates |= TGL_UPDATE_FLAGS; + } + C->flags = flags & 0xffff; + + if (DS_U->title) { + if (C->title) { + tfree_str (C->title); + } + C->title = DS_STR_DUP (DS_U->title); + + if (C->print_title) { + tglp_peer_delete_name (TLS, (void *)C); + tfree_str (C->print_title); + } + C->print_title = TLS->callback.create_print_name (TLS, C->id, C->title, 0, 0, 0); + tglp_peer_insert_name (TLS, (void *)C); + + updates |= TGL_UPDATE_TITLE; + } + + if (DS_U->user_num) { + C->users_num = DS_LVAL (DS_U->user_num); + } + + if (DS_U->date) { + C->date = DS_LVAL (DS_U->date); + } + + if (DS_U->chat_photo) { + tglf_fetch_file_location_new (TLS, &C->photo_big, DS_U->chat_photo->photo_big); + tglf_fetch_file_location_new (TLS, &C->photo_small, DS_U->chat_photo->photo_small); + updates |= TGL_UPDATE_PHOTO; + } + + if (DS_U->photo) { + if (C->photo) { + tgls_free_photo (TLS, C->photo); + } + C->photo = tglf_fetch_alloc_photo_new (TLS, DS_U->photo); + C->flags |= TGLPF_HAS_PHOTO; + updates |= TGL_UPDATE_PHOTO; + } + + if (DS_U->admin) { + C->admin_id = DS_LVAL (DS_U->admin); + updates |= TGL_UPDATE_ADMIN; + } + + if (DS_U->version) { + C->version = DS_LVAL (DS_U->version); + + if (C->user_list) { tfree (C->user_list, 12 * C->user_list_size); } + + C->user_list_size = DS_LVAL (DS_U->participants->cnt); + C->user_list = talloc (12 * C->user_list_size); + + int i; + for (i = 0; i < C->user_list_size; i++) { + C->user_list[i].user_id = DS_LVAL (DS_U->participants->data[i]->user_id); + C->user_list[i].inviter_id = DS_LVAL (DS_U->participants->data[i]->inviter_id); + C->user_list[i].date = DS_LVAL (DS_U->participants->data[i]->date); + } + + updates |= TGL_UPDATE_MEMBERS; + } + + if (DS_U->last_read_in) { + C->last_read_in = DS_LVAL (DS_U->last_read_in); + tgls_messages_mark_read (TLS, C->last, 0, C->last_read_in); + } + + if (DS_U->last_read_out) { + C->last_read_out = DS_LVAL (DS_U->last_read_out); + tgls_messages_mark_read (TLS, C->last, TGLMF_OUT, C->last_read_out); + } + + + if (TLS->callback.chat_update && updates) { + TLS->callback.chat_update (TLS, C, updates); + } + return 0; +} + +static int fetch_comb_binlog_chat_add_participant (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_CHAT (DS_LVAL (DS_U->id)); + tgl_peer_t *_C = tgl_peer_get (TLS, id); + assert (_C && (_C->flags & TGLPF_CREATED)); + struct tgl_chat *C = &_C->chat; + + int version = DS_LVAL (DS_U->version); + int user = DS_LVAL (DS_U->user_id); + int inviter = DS_LVAL (DS_U->inviter_id); + int date = DS_LVAL (DS_U->date); + + + if (C->user_list_version > version) { return 0; } + + int i; + for (i = 0; i < C->user_list_size; i++) { + if (C->user_list[i].user_id == user) { + return 0; + } + } + + C->user_list_size ++; + C->user_list = trealloc (C->user_list, 12 * C->user_list_size - 12, 12 * C->user_list_size); + C->user_list[C->user_list_size - 1].user_id = user; + C->user_list[C->user_list_size - 1].inviter_id = inviter; + C->user_list[C->user_list_size - 1].date = date; + C->user_list_version = version; + + if (TLS->callback.chat_update) { + TLS->callback.chat_update (TLS, C, TGL_UPDATE_MEMBERS); + } + return 0; +} + +static int fetch_comb_binlog_chat_del_participant (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_id_t id = TGL_MK_CHAT (DS_LVAL (DS_U->id)); + tgl_peer_t *_C = tgl_peer_get (TLS, id); + assert (_C && (_C->flags & TGLPF_CREATED)); + struct tgl_chat *C = &_C->chat; + + int version = DS_LVAL (DS_U->version); + int user = DS_LVAL (DS_U->user_id); + if (C->user_list_version > version) { return 0; } + + int i; + for (i = 0; i < C->user_list_size; i++) { + if (C->user_list[i].user_id == user) { + struct tgl_chat_user t; + t = C->user_list[i]; + C->user_list[i] = C->user_list[C->user_list_size - 1]; + C->user_list[C->user_list_size - 1] = t; + } + } + if (C->user_list[C->user_list_size - 1].user_id != user) { return 0; } + + assert (C->user_list[C->user_list_size - 1].user_id == user); + C->user_list_size --; + C->user_list = trealloc (C->user_list, 12 * C->user_list_size + 12, 12 * C->user_list_size); + C->user_list_version = version; + + if (TLS->callback.chat_update) { + TLS->callback.chat_update (TLS, C, TGL_UPDATE_MEMBERS); + } + return 0; +} + +static int fetch_comb_binlog_message_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_U->lid)); + int flags = DS_LVAL (DS_U->flags); + + if (flags & (1 << 16)) { + if (!M) { + M = tglm_message_alloc (TLS, DS_LVAL (DS_U->lid)); + } + assert (!(M->flags & TGLMF_CREATED)); + } else { + assert (M->flags & TGLMF_CREATED); + } + + assert (flags & TGLMF_CREATED); + assert (!(M->flags & TGLMF_ENCRYPTED)); + assert (!(flags & TGLMF_ENCRYPTED)); + + if ((M->flags & TGLMF_PENDING) && !(flags & TGLMF_PENDING)){ + tglm_message_remove_unsent (TLS, M); + } + if (!(M->flags & TGLMF_PENDING) && (flags & TGLMF_PENDING)){ + tglm_message_insert_unsent (TLS, M); + } + + if ((M->flags & TGLMF_UNREAD) && !(flags & TGLMF_UNREAD)) { + M->flags = (flags & 0xffff) | TGLMF_UNREAD; + } else { + M->flags = (flags & 0xffff); + } + + if (DS_U->from_id) { + M->from_id = TGL_MK_USER (DS_LVAL (DS_U->from_id)); + } + if (DS_U->to_type) { + assert (flags & 0x10000); + M->to_id = tgl_set_peer_id (DS_LVAL (DS_U->to_type), DS_LVAL (DS_U->to_id)); + assert (DS_LVAL (DS_U->to_type) != TGL_PEER_ENCR_CHAT); + } + + if (DS_U->date) { + M->date = DS_LVAL (DS_U->date); + } + + if (DS_U->fwd_from_id) { + M->fwd_from_id = TGL_MK_USER (DS_LVAL (DS_U->fwd_from_id)); + M->fwd_date = DS_LVAL (DS_U->fwd_date); + } + + if (DS_U->action) { + tglf_fetch_message_action_new (TLS, &M->action, DS_U->action); + M->flags |= TGLMF_SERVICE; + } + + if (DS_U->message) { + M->message_len = DS_U->message->len; + M->message = DS_STR_DUP (DS_U->message); + assert (!(M->flags & TGLMF_SERVICE)); + } + + if (DS_U->media) { + tglf_fetch_message_media_new (TLS, &M->media, DS_U->media); + assert (!(M->flags & TGLMF_SERVICE)); + } + + if (DS_U->reply_id) { + M->reply_id = DS_LVAL (DS_U->reply_id); + } + + if (flags & 0x10000) { + tglm_message_insert (TLS, M); + } + + if (!(flags & TGLMF_UNREAD) && (M->flags & TGLMF_UNREAD)) { + tgls_messages_mark_read (TLS, M, M->flags & TGLMF_OUT, M->id); + } + + if (DS_U->reply_markup) { + M->reply_markup = tglf_fetch_alloc_reply_markup (TLS, M->next, DS_U->reply_markup); + } + return 0; +} + +static int fetch_comb_binlog_message_encr_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_U->lid)); + int flags = DS_LVAL (DS_U->flags); + + if (flags & (1 << 16)) { + if (!M) { + M = tglm_message_alloc (TLS, DS_LVAL (DS_U->lid)); + } else { + assert (!(M->flags & TGLMF_CREATED)); + } + assert (!(M->flags & TGLMF_CREATED)); + } else { + assert (M->flags & TGLMF_CREATED); + } + + assert (flags & TGLMF_CREATED); + assert (flags & TGLMF_ENCRYPTED); + + if ((M->flags & TGLMF_PENDING) && !(flags & TGLMF_PENDING)){ + tglm_message_remove_unsent (TLS, M); + } + if (!(M->flags & TGLMF_PENDING) && (flags & TGLMF_PENDING)){ + tglm_message_insert_unsent (TLS, M); + } + + M->flags = flags & 0xffff; + + if (DS_U->from_id) { + M->from_id = TGL_MK_USER (DS_LVAL (DS_U->from_id)); + } + if (DS_U->to_type) { + assert (flags & 0x10000); + M->to_id = tgl_set_peer_id (DS_LVAL (DS_U->to_type), DS_LVAL (DS_U->to_id)); + } + + if (DS_U->date) { + M->date = DS_LVAL (DS_U->date); + } + + struct tgl_secret_chat *E = (void *)tgl_peer_get (TLS, M->to_id); + assert (E); + + if (DS_U->message) { + M->message_len = DS_U->message->len; + M->message = DS_STR_DUP (DS_U->message); + assert (!(M->flags & TGLMF_SERVICE)); + } + + if (DS_U->encr_media) { + tglf_fetch_message_media_encrypted_new (TLS, &M->media, DS_U->encr_media); + assert (!(M->flags & TGLMF_SERVICE)); + } + + if (DS_U->encr_action) { + tglf_fetch_message_action_encrypted_new (TLS, &M->action, DS_U->encr_action); + M->flags |= TGLMF_SERVICE; + } + + if (DS_U->file) { + tglf_fetch_encrypted_message_file_new (TLS, &M->media, DS_U->file); + assert (!(M->flags & TGLMF_SERVICE)); + } + + if (DS_U->encr_action && !(M->flags & TGLMF_OUT) && M->action.type == tgl_message_action_notify_layer) { + E->layer = M->action.layer; + } + + if ((flags & TGLMF_CREATE) && (flags & TGLMF_OUT)) { + E->out_seq_no ++; + } + + if (flags & 0x10000) { + tglm_message_insert (TLS, M); + } + return 0; +} + +static int fetch_comb_binlog_set_msg_id (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_U->old_id)); + assert (M); + if (M->flags & TGLMF_PENDING) { + tglm_message_remove_unsent (TLS, M); + M->flags &= ~TGLMF_PENDING; + } + + tglm_message_remove_tree (TLS, M); + tglm_message_del_peer (TLS, M); + + M->id = DS_LVAL (DS_U->new_id); + if (tgl_message_get (TLS, M->id)) { + tglm_message_del_use (TLS, M); + tgls_free_message (TLS, M); + } else { + tglm_message_insert_tree (TLS, M); + tglm_message_add_peer (TLS, M); + } + return 0; +} + +static int fetch_comb_binlog_message_delete (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_U->lid)); + assert (M); + if (M->flags & TGLMF_PENDING) { + tglm_message_remove_unsent (TLS, M); + M->flags &= ~TGLMF_PENDING; + } + tglm_message_remove_tree (TLS, M); + tglm_message_del_peer (TLS, M); + tglm_message_del_use (TLS, M); + tgls_free_message (TLS, M); + return 0; +} + +static int fetch_comb_binlog_msg_update (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_U->lid)); + if (!M) { return 0; } + assert (M); + + if (!(M->flags & TGLMF_ENCRYPTED)) { + if (TLS->max_msg_id < M->id) { + TLS->max_msg_id = M->id; + } + } + + if (TLS->callback.msg_receive) { + TLS->callback.msg_receive (TLS, M); + } + return 0; +} + +static int fetch_comb_binlog_reset_authorization (struct tgl_state *TLS, void *extra) { + int i; + for (i = 0; i <= TLS->max_dc_num; i++) if (TLS->DC_list[i]) { + struct tgl_dc *D = TLS->DC_list[i]; + D->flags = 0; + D->state = st_init; + D->auth_key_id = D->temp_auth_key_id = 0; + } + TLS->seq = 0; + TLS->qts = 0; + return 0; +} + +static int fetch_comb_binlog_encr_chat_exchange_new (struct tgl_state *TLS, struct tl_ds_binlog_update *DS_U) { + tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_ENCR_CHAT (DS_LVAL (DS_U->id))); + assert (P); + if (DS_U->state) { + P->encr_chat.exchange_state = DS_LVAL (DS_U->state); + } + if (DS_U->exchange_id) { + P->encr_chat.exchange_id = DS_LVAL (DS_U->exchange_id); + } + + static unsigned char sha_buffer[20]; + switch (P->encr_chat.exchange_state) { + case tgl_sce_requested: + tglf_fetch_int_tuple (P->encr_chat.exchange_key, DS_U->key->key, 64); + break; + case tgl_sce_accepted: + tglf_fetch_int_tuple (P->encr_chat.exchange_key, DS_U->key->key, 64); + + SHA1 ((unsigned char *)P->encr_chat.exchange_key, 256, sha_buffer); + P->encr_chat.exchange_key_fingerprint = *(long long *)(sha_buffer + 12); + break; + case tgl_sce_committed: + memcpy (P->encr_chat.exchange_key, P->encr_chat.key, 256); + P->encr_chat.exchange_key_fingerprint = P->encr_chat.key_fingerprint; + + tglf_fetch_int_tuple (P->encr_chat.key, DS_U->key->key, 64); + + SHA1 ((unsigned char *)P->encr_chat.key, 256, sha_buffer); + P->encr_chat.key_fingerprint = *(long long *)(sha_buffer + 12); + break; + case tgl_sce_confirmed: + P->encr_chat.exchange_state = tgl_sce_none; + if (P->encr_chat.exchange_state != tgl_sce_committed) { + memcpy (P->encr_chat.key, P->encr_chat.exchange_key, 256); + P->encr_chat.key_fingerprint = P->encr_chat.exchange_key_fingerprint; + } + break; + case tgl_sce_aborted: + P->encr_chat.exchange_state = tgl_sce_none; + if (P->encr_chat.exchange_state == tgl_sce_committed) { + memcpy (P->encr_chat.key, P->encr_chat.exchange_key, 256); + P->encr_chat.key_fingerprint = P->encr_chat.exchange_key_fingerprint; + } + break; + default: + assert (0); + } + return 0; +} + +#define FETCH_COMBINATOR_FUNCTION(NAME) \ + case CODE_ ## NAME:\ + ok = fetch_comb_ ## NAME (TLS, DS_U); \ + break; \ + + +static void replay_log_event (struct tgl_state *TLS) { + assert (rptr < wptr); + int op = *rptr; + + vlogprintf (E_DEBUG, "replay_log_event: log_pos=%"_PRINTF_INT64_"d, op=0x%08x\n", binlog_pos, op); + + in_ptr = rptr; + in_end = wptr; + if (skip_type_any (TYPE_TO_PARAM (binlog_update)) < 0) { + vlogprintf (E_ERROR, "Can not replay at %"_PRINTF_INT64_"d (magic = 0x%08x)\n", binlog_pos, *rptr); + assert (0); + } + int *end = in_ptr; + in_end = in_ptr; + in_ptr = rptr; + struct tl_ds_binlog_update *DS_U = fetch_ds_type_binlog_update (TYPE_TO_PARAM (binlog_update)); + assert (in_ptr == end); + + int ok = -1; + + switch (op) { + FETCH_COMBINATOR_FUNCTION (binlog_start) + FETCH_COMBINATOR_FUNCTION (binlog_dc_option) + FETCH_COMBINATOR_FUNCTION (binlog_dc_option_new) + FETCH_COMBINATOR_FUNCTION (binlog_auth_key) + FETCH_COMBINATOR_FUNCTION (binlog_default_dc) + FETCH_COMBINATOR_FUNCTION (binlog_dc_signed) + + FETCH_COMBINATOR_FUNCTION (binlog_our_id) + + FETCH_COMBINATOR_FUNCTION (binlog_set_dh_params) + FETCH_COMBINATOR_FUNCTION (binlog_set_pts) + FETCH_COMBINATOR_FUNCTION (binlog_set_qts) + FETCH_COMBINATOR_FUNCTION (binlog_set_date) + FETCH_COMBINATOR_FUNCTION (binlog_set_seq) + + FETCH_COMBINATOR_FUNCTION (binlog_user_new) + FETCH_COMBINATOR_FUNCTION (binlog_user_delete) + + FETCH_COMBINATOR_FUNCTION (binlog_chat_new) + //FETCH_COMBINATOR_FUNCTION (binlog_chat_delete) + + FETCH_COMBINATOR_FUNCTION (binlog_encr_chat_new) + FETCH_COMBINATOR_FUNCTION (binlog_encr_chat_delete) + FETCH_COMBINATOR_FUNCTION (binlog_chat_add_participant) + FETCH_COMBINATOR_FUNCTION (binlog_chat_del_participant) + + FETCH_COMBINATOR_FUNCTION (binlog_message_new) + FETCH_COMBINATOR_FUNCTION (binlog_message_encr_new) + FETCH_COMBINATOR_FUNCTION (binlog_message_delete) + FETCH_COMBINATOR_FUNCTION (binlog_set_msg_id) + + FETCH_COMBINATOR_FUNCTION (binlog_encr_chat_exchange_new) + + FETCH_COMBINATOR_FUNCTION (binlog_msg_update) + FETCH_COMBINATOR_FUNCTION (binlog_reset_authorization) + default: + vlogprintf (E_ERROR, "Unknown op 0x%08x\n", op); + assert (0); + } + assert (ok >= 0); + + free_ds_type_binlog_update (DS_U, TYPE_TO_PARAM (binlog_update)); + assert (in_ptr == end); + //assert (in_ptr == in_end); + binlog_pos += (in_ptr - rptr) * 4; + rptr = in_ptr; +} + +static void create_new_binlog (struct tgl_state *TLS) { + clear_packet (); + //static int s[1000]; + + //packet_ptr = s; + out_int (CODE_binlog_start); + if (TLS->test_mode) { + out_int (CODE_binlog_dc_option); + out_int (1); + out_string (""); + out_string (TG_SERVER_TEST_1); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (2); + out_string (""); + out_string (TG_SERVER_TEST_2); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (3); + out_string (""); + out_string (TG_SERVER_TEST_3); + out_int (443); + out_int (CODE_binlog_default_dc); + out_int (2); + } else { + out_int (CODE_binlog_dc_option); + out_int (1); + out_string (""); + out_string (TG_SERVER_1); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (2); + out_string (""); + out_string (TG_SERVER_2); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (3); + out_string (""); + out_string (TG_SERVER_3); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (4); + out_string (""); + out_string (TG_SERVER_4); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (5); + out_string (""); + out_string (TG_SERVER_5); + out_int (443); + out_int (CODE_binlog_default_dc); + out_int (2); + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + int fd = 0; + if(_sopen_s (&fd, TLS->binlog_name, _O_WRONLY | _O_EXCL | _O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0 ) { +#else + int fd = open (TLS->binlog_name, O_WRONLY | O_EXCL | O_CREAT, 0600); + if (fd < 0) { +#endif + perror ("Write new binlog"); + exit (2); + } + assert (write (fd, packet_buffer, (packet_ptr - packet_buffer) * 4) == (packet_ptr - packet_buffer) * 4); + close (fd); +} + + +void tgl_replay_log (struct tgl_state *TLS) { + if (!TLS->binlog_enabled) { return; } +#if defined(WIN32) || defined(_WIN32) + if (INVALID_FILE_ATTRIBUTES == GetFileAttributesA (TLS->binlog_name) && GetLastError () == ERROR_FILE_NOT_FOUND) { +#else + if (access (TLS->binlog_name, F_OK) < 0) { +#endif + printf ("No binlog found. Creating new one\n"); + create_new_binlog (TLS); + } +#if defined(_MSC_VER) && _MSC_VER >= 1400 + int fd = 0; + if (_sopen_s(&fd, TLS->binlog_name, _O_RDONLY | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0) { +#elif defined(WIN32) || defined(_WIN32) + int fd = open (TLS->binlog_name, O_RDONLY | O_BINARY); + if (fd < 0) { +#else + int fd = open (TLS->binlog_name, O_RDONLY); + if (fd < 0) { +#endif + perror ("binlog open"); + exit (2); + } + int end = 0; + in_replay_log = 1; + while (1) { + if (!end && wptr - rptr < MAX_LOG_EVENT_SIZE / 4) { + if (wptr == rptr) { + wptr = rptr = binlog_buffer; + } else { + int x = wptr - rptr; + memcpy (binlog_buffer, rptr, 4 * x); + wptr -= (rptr - binlog_buffer); + rptr = binlog_buffer; + } + int l = (binlog_buffer + BINLOG_BUFFER_SIZE - wptr) * 4; + int k = read (fd, wptr, l); + if (k < 0) { + perror ("read binlog"); + exit (2); + } + assert (!(k & 3)); + if (k < l) { + end = 1; + } + wptr += (k / 4); + } + if (wptr == rptr) { break; } + replay_log_event (TLS); + } + in_replay_log = 0; + close (fd); +} + +//static int b_packet_buffer[PACKET_BUFFER_SIZE]; + +void tgl_reopen_binlog_for_writing (struct tgl_state *TLS) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (_sopen_s (&TLS->binlog_fd, TLS->binlog_name, _O_WRONLY | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0) { +#elif defined(WIN32) || defined(_WIN32) + TLS->binlog_fd = open (TLS->binlog_name, O_WRONLY | _O_BINARY); + if (TLS->binlog_fd < 0) { +#else + TLS->binlog_fd = open (TLS->binlog_name, O_WRONLY); + if (TLS->binlog_fd < 0) { +#endif + perror ("binlog open"); + exit (2); + } + + assert (lseek (TLS->binlog_fd, binlog_pos, SEEK_SET) == binlog_pos); +#if defined(WIN32) || defined(_WIN32) + HANDLE h = INVALID_HANDLE_VALUE; + DWORD size_lower, size_upper; + DWORD err = 0; + OVERLAPPED ovlp; + int flags = 0; + + h = (HANDLE)_get_osfhandle(TLS->binlog_fd); + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + goto error; + } + + size_lower = GetFileSize (h, &size_upper); + if (size_lower == INVALID_FILE_SIZE) { + goto get_err; + } + + memset (&ovlp, 0, sizeof ovlp); + flags |= LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY; + + if (!LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp)) { + goto get_err; + } + return; + +error: + perror ("get lock"); + exit(2); + +get_err: + err = GetLastError(); + switch (err) + { + case ERROR_LOCK_VIOLATION: + errno = EAGAIN; + break; + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case ERROR_BAD_COMMAND: + errno = EINVAL; + break; + default: + errno = err; + } + goto error; +#else + if (flock (TLS->binlog_fd, LOCK_EX | LOCK_NB) < 0) { + perror ("get lock"); + exit (2); + } +#endif +} + +static void add_log_event (struct tgl_state *TLS, const int *data, int len) { + vlogprintf (E_DEBUG, "Add log event: magic = 0x%08x, len = %d\n", data[0], len); + assert (!(len & 3)); + int *ev = talloc (len); + memcpy (ev, data, len); + rptr = (void *)ev; + wptr = rptr + (len / 4); + int *in = in_ptr; + int *end = in_end; + replay_log_event (TLS); + if (rptr != wptr) { + vlogprintf (E_ERROR, "Unread %"_PRINTF_INT64_"d ints. Len = %d\n", (long long)(wptr - rptr), len); + assert (rptr == wptr); + } + if (TLS->binlog_enabled) { + assert (TLS->binlog_fd > 0); + assert (write (TLS->binlog_fd, ev, len) == len); + } + tfree (ev, len); + in_ptr = in; + in_end = end; +} + +void bl_do_dc_option_new (struct tgl_state *TLS, int flags, int id, const char *name, int l1, const char *ip, int l2, int port) { + struct tgl_dc *DC = TLS->DC_list[id]; + + if (DC) { + struct tgl_dc_option *O = DC->options[flags & 3]; + while (O) { + if (!strncmp (O->ip, ip, l2)) { + return; + } + O = O->next; + } + } + + clear_packet (); + out_int (CODE_binlog_dc_option_new); + out_int (flags); + out_int (id); + + out_cstring (name, l1); + out_cstring (ip, l2); + out_int (port); + + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_dc_option (struct tgl_state *TLS, int id, const char *name, int l1, const char *ip, int l2, int port) { + bl_do_dc_option_new (TLS, 0, id, name, l1, ip, l2, port); +} + +void bl_do_set_working_dc (struct tgl_state *TLS, int num) { + int *ev = alloc_log_event (8); + ev[0] = CODE_binlog_default_dc; + ev[1] = num; + add_log_event (TLS, ev, 8); +} + +void bl_do_dc_signed (struct tgl_state *TLS, int id) { + clear_packet (); + out_int (CODE_binlog_dc_signed); + out_int (id); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_our_id (struct tgl_state *TLS, int id) { + if (TLS->our_id) { + assert (TLS->our_id == id); + return; + } + + clear_packet (); + out_int (CODE_binlog_our_id); + out_int (id); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_dh_params (struct tgl_state *TLS, int root, unsigned char prime[], int version) { + clear_packet (); + out_int (CODE_binlog_set_dh_params); + out_int (root); + out_ints ((void *)prime, 64); + out_int (version); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_pts (struct tgl_state *TLS, int pts) { + if (TLS->locks & TGL_LOCK_DIFF) { return; } + if (pts <= TLS->pts) { return; } + + clear_packet (); + out_int (CODE_binlog_set_pts); + out_int (pts); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_qts (struct tgl_state *TLS, int qts) { + if (TLS->locks & TGL_LOCK_DIFF) { return; } + if (qts <= TLS->qts) { return; } + + clear_packet (); + out_int (CODE_binlog_set_qts); + out_int (qts); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_date (struct tgl_state *TLS, int date) { + if (TLS->locks & TGL_LOCK_DIFF) { return; } + if (date <= TLS->date) { return; } + + clear_packet (); + out_int (CODE_binlog_set_date); + out_int (date); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_seq (struct tgl_state *TLS, int seq) { + if (TLS->locks & TGL_LOCK_DIFF) { return; } + if (seq <= TLS->seq) { return; } + + clear_packet (); + out_int (CODE_binlog_set_seq); + out_int (seq); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_msg_id (struct tgl_state *TLS, struct tgl_message *M, int id) { + if (M->id == id) { return; } + + clear_packet (); + out_int (CODE_binlog_set_msg_id); + out_long (M->id); + out_int (id); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_user_delete (struct tgl_state *TLS, struct tgl_user *U) { + if (U->flags & TGLUF_DELETED) { return; } + + clear_packet (); + out_int (CODE_binlog_user_delete); + out_int (tgl_get_peer_id (U->id)); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_encr_chat_delete (struct tgl_state *TLS, struct tgl_secret_chat *U) { + if (!(U->flags & TGLPF_CREATED) || U->state == sc_deleted || U->state == sc_none) { return; } + + clear_packet (); + out_int (CODE_binlog_encr_chat_delete); + out_int (tgl_get_peer_id (U->id)); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_chat_add_user (struct tgl_state *TLS, struct tgl_chat *C, int version, int user, int inviter, int date) { + if (C->user_list_version >= version || !C->user_list_version) { return; } + + clear_packet (); + out_int (CODE_binlog_chat_add_participant); + out_int (tgl_get_peer_id (C->id)); + out_int (version); + out_int (user); + out_int (inviter); + out_int (date); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_chat_del_user (struct tgl_state *TLS, struct tgl_chat *C, int version, int user) { + if (C->user_list_version >= version || !C->user_list_version) { return; } + + clear_packet (); + out_int (CODE_binlog_chat_del_participant); + out_int (tgl_get_peer_id (C->id)); + out_int (version); + out_int (user); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_create_message_new (struct tgl_state *TLS, long long id, int *from_id, int *to_type, int *to_id, int *fwd_from_id, int *fwd_date, int *date, const char *message, int message_len, struct tl_ds_message_media *media, struct tl_ds_message_action *action, int *reply_id, struct tl_ds_reply_markup *reply_markup, int flags) { + clear_packet (); + assert (!(flags & 0xfffe0000)); + + out_int (CODE_binlog_message_new); + int *flags_p = packet_ptr; + out_int (flags); + assert (*flags_p == flags); + + out_long (id); + + if (from_id) { + assert (to_type); + assert (to_id); + (*flags_p) |= (1 << 17); + out_int (*from_id); + out_int (*to_type); + out_int (*to_id); + } + + if (fwd_from_id) { + assert (fwd_date); + (*flags_p) |= (1 << 18); + out_int (*fwd_from_id); + out_int (*fwd_date); + } + + if (date) { + (*flags_p) |= (1 << 19); + out_int (*date); + } + + if (message) { + (*flags_p) |= (1 << 20); + out_cstring (message, message_len); + } + + if (media) { + (*flags_p) |= (1 << 21); + store_ds_type_message_media (media, TYPE_TO_PARAM (message_media)); + } + + if (action) { + (*flags_p) |= (1 << 22); + + store_ds_type_message_action (action, TYPE_TO_PARAM (message_action)); + } + + if (reply_id) { + (*flags_p) |= (1 << 23); + out_int (*reply_id); + } + + if (reply_markup) { + (*flags_p) |= (1 << 24); + store_ds_type_reply_markup (reply_markup, TYPE_TO_PARAM (reply_markup)); + } + + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_create_message_encr_new (struct tgl_state *TLS, long long id, int *from_id, int *to_type, int *to_id, int *date, const char *message, int message_len, struct tl_ds_decrypted_message_media *media, struct tl_ds_decrypted_message_action *action, struct tl_ds_encrypted_file *file, int flags) { + clear_packet (); + assert (!(flags & 0xfffe0000)); + + out_int (CODE_binlog_message_encr_new); + int *flags_p = packet_ptr; + out_int (flags); + assert (flags & TGLMF_ENCRYPTED); + assert (*flags_p == flags); + + out_long (id); + + if (from_id) { + assert (to_id); + assert (to_type); + (*flags_p) |= (1 << 17); + out_int (*from_id); + out_int (*to_type); + out_int (*to_id); + } + + if (date) { + (*flags_p) |= (1 << 19); + out_int (*date); + } + + if (message) { + (*flags_p) |= (1 << 20); + out_cstring (message, message_len); + } + + if (media) { + (*flags_p) |= (1 << 21); + store_ds_type_decrypted_message_media (media, TYPE_TO_PARAM (decrypted_message_media)); + } + + if (action) { + (*flags_p) |= (1 << 22); + store_ds_type_decrypted_message_action (action, TYPE_TO_PARAM (decrypted_message_action)); + } + + if (file) { + (*flags_p) |= (1 << 23); + store_ds_type_encrypted_file (file, TYPE_TO_PARAM (encrypted_file)); + } + + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_message_delete (struct tgl_state *TLS, struct tgl_message *M) { + clear_packet (); + out_int (CODE_binlog_message_delete); + out_long (M->id); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_msg_update (struct tgl_state *TLS, long long id) { + clear_packet (); + out_int (CODE_binlog_msg_update); + out_long (id); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_reset_authorization (struct tgl_state *TLS) { + clear_packet (); + out_int (CODE_binlog_reset_authorization); + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_encr_chat_exchange_new (struct tgl_state *TLS, struct tgl_secret_chat *E, long long *exchange_id, const void *key, int *state) { + clear_packet (); + + out_int (CODE_binlog_encr_chat_exchange_new); + out_int (tgl_get_peer_id (E->id)); + + int *flags_p = packet_ptr; + out_int (0); + + if (exchange_id) { + *flags_p |= (1 << 17); + out_long (*exchange_id); + } + + if (key) { + *flags_p |= (1 << 18); + out_ints ((void *)key, 64); + } + + if (state) { + *flags_p |= (1 << 19); + out_int (*state); + } + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); +} + +void bl_do_set_auth_key (struct tgl_state *TLS, int num, unsigned char *buf) { + int *ev = alloc_log_event (8 + 8 + 256); + ev[0] = CODE_binlog_auth_key; + ev[1] = num; + //*(long long *)(ev + 2) = fingerprint; + memcpy (ev + 2, buf, 256); + add_log_event (TLS, ev, 8 + 256); +} + +void bl_do_user_new (struct tgl_state *TLS, int id, long long *access_hash, const char *first_name, int first_name_len, const char *last_name, int last_name_len, const char *phone, int phone_len, const char *username, int username_len, struct tl_ds_photo *photo, const char *real_first_name, int real_first_name_len, const char *real_last_name, int real_last_name_len, struct tl_ds_user_profile_photo *profile_photo, int *last_read_in, int *last_read_out, struct tl_ds_bot_info *bot_info, int flags) { + tgl_peer_t *PP = tgl_peer_get (TLS, TGL_MK_USER (id)); + struct tgl_user *P = &PP->user; + + if (flags == TGL_FLAGS_UNCHANGED) { + flags = P->flags & 0xffff; + } + + clear_packet (); + out_int (CODE_binlog_user_new); + + int *flags_p = packet_ptr; + + assert (!(flags & 0xfffe0000)); + out_int (flags); + out_int (id); + + if (access_hash) { + if (!P || P->access_hash != *access_hash) { + out_long (*access_hash); + (*flags_p) |= (1 << 17); + } + } + + if (first_name) { + if (!P || !P->first_name || !P->last_name || mystreq1 (P->first_name, first_name, first_name_len) || mystreq1 (P->last_name, last_name, last_name_len)) { + out_cstring (first_name, first_name_len); + out_cstring (last_name, last_name_len); + + (*flags_p) |= (1 << 18); + } + } + + if (phone) { + if (!P || !P->phone || mystreq1 (P->phone, phone, phone_len)) { + out_cstring (phone, phone_len); + (*flags_p) |= (1 << 19); + } + } + + if (username) { + if (!P || !P->username || mystreq1 (P->username, username, username_len)) { + out_cstring (username, username_len); + (*flags_p) |= (1 << 20); + } + } + + if (photo) { + if (!P || !P->photo || P->photo->id != DS_LVAL (photo->id)) { + store_ds_type_photo (photo, TYPE_TO_PARAM (photo)); + (*flags_p) |= (1 << 21); + } + } + + if (real_first_name) { + assert (real_last_name); + if (!P || !P->real_first_name || !P->real_last_name || mystreq1 (P->real_first_name, real_first_name, real_first_name_len) || mystreq1 (P->real_last_name, real_last_name, real_last_name_len)) { + out_cstring (real_first_name, real_first_name_len); + out_cstring (real_last_name, real_last_name_len); + + (*flags_p) |= (1 << 22); + } + } + + if (profile_photo) { + if (!P || P->photo_id != DS_LVAL (profile_photo->photo_id)) { + store_ds_type_user_profile_photo (profile_photo, TYPE_TO_PARAM (user_profile_photo)); + (*flags_p) |= (1 << 23); + } + } + + if (last_read_in) { + if (!P || P->last_read_in < *last_read_in) { + out_int (*last_read_in); + (*flags_p) |= (1 << 24); + } + } + + if (last_read_out) { + if (!P || P->last_read_out < *last_read_out) { + out_int (*last_read_out); + (*flags_p) |= (1 << 25); + } + } + + if (bot_info) { + if (!P || !P->bot_info || P->bot_info->version != DS_LVAL (bot_info->version)) { + store_ds_type_bot_info (bot_info, TYPE_TO_PARAM (bot_info)); + (*flags_p) |= (1 << 26); + } + } + + if (((*flags_p) & 0xffff0000) || !P || (P->flags & 0xffff) != flags) { + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); + } +} + +void bl_do_chat_new (struct tgl_state *TLS, int id, const char *title, int title_len, int *user_num, int *date, int *version, struct tl_ds_vector *participants, struct tl_ds_chat_photo *chat_photo, struct tl_ds_photo *photo, int *admin, int *last_read_in, int *last_read_out, int flags) { + tgl_peer_t *PP = tgl_peer_get (TLS, TGL_MK_CHAT (id)); + struct tgl_chat *P = &PP->chat; + + if (flags == TGL_FLAGS_UNCHANGED) { + flags = P ? (P->flags & 0xffff) : 0; + } + + clear_packet (); + out_int (CODE_binlog_chat_new); + + int *flags_p = packet_ptr; + + assert (!(flags & 0xfffe0000)); + out_int (flags); + out_int (id); + + if (title) { + if (!P || !P->title || mystreq1 (P->title, title, title_len)) { + out_cstring (title, title_len); + (*flags_p) |= (1 << 17); + } + } + + if (user_num) { + if (!P || P->users_num != *user_num) { + out_int (*user_num); + (*flags_p) |= (1 << 18); + } + } + + if (date) { + if (!P || P->date != *date) { + out_int (*date); + (*flags_p) |= (1 << 19); + } + } + + if (version) { + assert (participants); + if (!P || *version != P->version) { + out_int (*version); + store_ds_type_vector (participants, TYPE_TO_PARAM_1 (vector, TYPE_TO_PARAM (chat_participant))); + (*flags_p) |= (1 << 20); + } + } + + if (chat_photo && chat_photo->photo_big) { + if (!P || DS_LVAL (chat_photo->photo_big->secret) != P->photo_big.secret) { + store_ds_type_chat_photo (chat_photo, TYPE_TO_PARAM (chat_photo)); + (*flags_p) |= (1 << 21); + } + } + + if (photo) { + if (!P || !P->photo || P->photo->id != DS_LVAL (photo->id)) { + store_ds_type_photo (photo, TYPE_TO_PARAM (photo)); + (*flags_p) |= (1 << 22); + } + } + + if (admin) { + if (!P || P->admin_id != *admin) { + out_int (*admin); + (*flags_p) |= (1 << 23); + } + } + + if (last_read_in) { + if (!P || P->last_read_in < *last_read_in) { + out_int (*last_read_in); + (*flags_p) |= (1 << 24); + } + } + + if (last_read_out) { + if (!P || P->last_read_out < *last_read_out) { + out_int (*last_read_out); + (*flags_p) |= (1 << 25); + } + } + + if (((*flags_p) & 0xffff0000) || !P || (P->flags & 0xffff) != flags) { + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); + } +} + +void bl_do_encr_chat_new (struct tgl_state *TLS, int id, long long *access_hash, int *date, int *admin, int *user_id, void *key, void *g_key, void *first_key_id, int *state, int *ttl, int *layer, int *in_seq_no, int *last_in_seq_no, int *out_seq_no, long long *key_fingerprint, int flags) { + tgl_peer_t *PP = tgl_peer_get (TLS, TGL_MK_ENCR_CHAT (id)); + struct tgl_secret_chat *P = PP ? &PP->encr_chat : NULL; + + if (flags == TGL_FLAGS_UNCHANGED) { + flags = P->flags & 0xffff; + } + + clear_packet (); + out_int (CODE_binlog_encr_chat_new); + + int *flags_p = packet_ptr; + + assert (!(flags & 0xfffe0000)); + out_int (flags); + out_int (id); + + if (access_hash) { + if (!P || P->access_hash != *access_hash) { + out_long (*access_hash); + (*flags_p) |= (1 << 17); + } + } + + if (date) { + if (!P || P->date != *date) { + out_int (*date); + (*flags_p) |= (1 << 18); + } + } + + if (admin) { + if (!P || P->admin_id != *admin) { + out_int (*admin); + (*flags_p) |= (1 << 19); + } + } + + if (user_id) { + if (!P || P->user_id != *user_id) { + out_int (*user_id); + (*flags_p) |= (1 << 20); + } + } + + if (key) { + if (!P || memcmp (P->key, key, 256)) { + out_ints (key, 64); + (*flags_p) |= (1 << 21); + } + } + + if (g_key) { + if (!P || !P->g_key || memcmp (P->g_key, g_key, 256)) { + out_ints (g_key, 64); + (*flags_p) |= (1 << 22); + } + } + + if (state) { + if (!P || (int)P->state != *state) { + out_int (*state); + (*flags_p) |= (1 << 23); + } + } + + if (ttl) { + if (!P || P->ttl != *ttl) { + out_int (*ttl); + (*flags_p) |= (1 << 24); + } + } + + if (layer) { + if (!P || P->layer != *layer) { + out_int (*layer); + (*flags_p) |= (1 << 25); + } + } + + if (in_seq_no || last_in_seq_no || out_seq_no) { + if (!P || (in_seq_no && P->in_seq_no != *in_seq_no) || + (out_seq_no && P->out_seq_no != *out_seq_no) || + (last_in_seq_no && P->last_in_seq_no != *last_in_seq_no)) { + + out_int (in_seq_no ? *in_seq_no : P ? P->in_seq_no : 0); + out_int (last_in_seq_no ? *last_in_seq_no : P ? P->last_in_seq_no : 0); + out_int (out_seq_no ? *out_seq_no : P ? P->out_seq_no : 0); + (*flags_p) |= (1 << 26); + } + } + + if (key_fingerprint) { + if (!P || P->key_fingerprint != *key_fingerprint) { + out_long (*key_fingerprint); + (*flags_p) |= (1 << 27); + } + } + + if (((*flags_p) & 0xffff0000) || !P || (P->flags & 0xffff) != flags) { + add_log_event (TLS, packet_buffer, 4 * (packet_ptr - packet_buffer)); + } +} |