/* 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 #include #include #include #include "tgl-structures.h" #include "mtproto-common.h" //#include "telegram.h" #include "tree.h" #include "crypto/aes.h" #include "crypto/bn.h" #include "crypto/sha.h" #include "queries.h" #include "tgl-binlog.h" #include "tgl-methods-in.h" #include "updates.h" #include "mtproto-client.h" #include "tgl.h" #include "auto.h" #include "auto/auto-types.h" #include "auto/auto-skip.h" #include "auto/auto-fetch-ds.h" #include "auto/auto-free-ds.h" static int id_cmp (struct tgl_message *M1, struct tgl_message *M2); #define peer_cmp(a,b) (tgl_cmp_peer_id (a->id, b->id)) #define peer_cmp_name(a,b) (strcmp (a->print_name, b->print_name)) static int random_id_cmp (struct tgl_message *L, struct tgl_message *R) { if (L->random_id < R->random_id) { return -1; } if (L->random_id > R->random_id) { return 1; } return 0; } static int temp_id_cmp (struct tgl_message *L, struct tgl_message *R) { if (L->temp_id < R->temp_id) { return -1; } if (L->temp_id > R->temp_id) { return 1; } return 0; } static int photo_id_cmp (struct tgl_photo *L, struct tgl_photo *R) { if (L->id < R->id) { return -1; } if (L->id > R->id) { return 1; } return 0; } static int document_id_cmp (struct tgl_document *L, struct tgl_document *R) { if (L->id < R->id) { return -1; } if (L->id > R->id) { return 1; } return 0; } static int webpage_id_cmp (struct tgl_webpage *L, struct tgl_webpage *R) { if (L->id < R->id) { return -1; } if (L->id > R->id) { return 1; } return 0; } DEFINE_TREE(peer,tgl_peer_t *,peer_cmp,0) DEFINE_TREE(peer_by_name,tgl_peer_t *,peer_cmp_name,0) DEFINE_TREE(message,struct tgl_message *,id_cmp,0) DEFINE_TREE(random_id,struct tgl_message *, random_id_cmp,0) DEFINE_TREE(temp_id,struct tgl_message *, temp_id_cmp,0) DEFINE_TREE(photo,struct tgl_photo *,photo_id_cmp,0) DEFINE_TREE(document,struct tgl_document *,document_id_cmp,0) DEFINE_TREE(webpage,struct tgl_webpage *,webpage_id_cmp,0) static void increase_peer_size (struct tgl_state *TLS); char *tgls_default_create_print_name (struct tgl_state *TLS, tgl_peer_id_t id, const char *a1, const char *a2, const char *a3, const char *a4) { const char *d[4]; d[0] = a1; d[1] = a2; d[2] = a3; d[3] = a4; static char buf[10000]; buf[0] = 0; int i; int p = 0; for (i = 0; i < 4; i++) { if (d[i] && strlen (d[i])) { p += tsnprintf (buf + p, 9999 - p, "%s%s", p ? "_" : "", d[i]); assert (p < 9990); } } char *s = buf; while (*s) { if (((unsigned char)*s) <= ' ') { *s = '_'; } if (*s == '#') { *s = '_'; } if (*s == '$') { *s = '_'; } if (*s == '@') { *s = '_'; } s++; } s = buf; int fl = strlen (s); int cc = 0; while (1) { tgl_peer_t *P = tgl_peer_get_by_name (TLS, s); if (!P || !tgl_cmp_peer_id (P->id, id)) { break; } cc ++; assert (cc <= 9999); tsnprintf (s + fl, 9999 - fl, "#%d", cc); } return tstrdup (s); } enum tgl_typing_status tglf_fetch_typing (struct tl_ds_send_message_action *DS_SMA) { if (!DS_SMA) { return 0; } switch (DS_SMA->magic) { case CODE_send_message_typing_action: return tgl_typing_typing; case CODE_send_message_cancel_action: return tgl_typing_cancel; case CODE_send_message_record_video_action: return tgl_typing_record_video; case CODE_send_message_upload_video_action: return tgl_typing_upload_video; case CODE_send_message_record_audio_action: return tgl_typing_record_audio; case CODE_send_message_upload_audio_action: return tgl_typing_upload_audio; case CODE_send_message_upload_photo_action: return tgl_typing_upload_photo; case CODE_send_message_upload_document_action: return tgl_typing_upload_document; case CODE_send_message_geo_location_action: return tgl_typing_geo; case CODE_send_message_choose_contact_action: return tgl_typing_choose_contact; default: assert (0); return tgl_typing_none; } } /*enum tgl_typing_status tglf_fetch_typing (void) { struct tl_ds_send_message_action *DS_SMA = fetch_ds_type_send_message_action (TYPE_TO_PARAM (send_message_action)); enum tgl_typing_status res = tglf_fetch_typing (DS_SMA); free_ds_type_send_message_action (DS_SMA, TYPE_TO_PARAM (send_message_action)); return res; }*/ /* {{{ Fetch */ tgl_peer_id_t tglf_fetch_peer_id (struct tgl_state *TLS, struct tl_ds_peer *DS_P) { switch (DS_P->magic) { case CODE_peer_user: return TGL_MK_USER (DS_LVAL (DS_P->user_id)); case CODE_peer_chat: return TGL_MK_CHAT (DS_LVAL (DS_P->chat_id)); case CODE_peer_channel: return TGL_MK_CHANNEL (DS_LVAL (DS_P->channel_id)); default: assert (0); exit (2); } } int tglf_fetch_file_location (struct tgl_state *TLS, struct tgl_file_location *loc, struct tl_ds_file_location *DS_FL) { if (!DS_FL) { return 0; } loc->dc = DS_LVAL (DS_FL->dc_id); loc->volume = DS_LVAL (DS_FL->volume_id); loc->local_id = DS_LVAL (DS_FL->local_id); loc->secret = DS_LVAL (DS_FL->secret); return 0; } int tglf_fetch_user_status (struct tgl_state *TLS, struct tgl_user_status *S, struct tgl_user *U, struct tl_ds_user_status *DS_US) { if (!DS_US) { return 0; } switch (DS_US->magic) { case CODE_user_status_empty: if (S->online) { tgl_insert_status_update (TLS, U); if (S->online == 1) { tgl_remove_status_expire (TLS, U); } } S->online = 0; S->when = 0; break; case CODE_user_status_online: { if (S->online != 1) { S->when = DS_LVAL (DS_US->expires); if (S->online) { tgl_insert_status_update (TLS, U); } tgl_insert_status_expire (TLS, U); S->online = 1; } else { if (DS_LVAL (DS_US->expires) != S->when) { S->when = DS_LVAL (DS_US->expires); tgl_remove_status_expire (TLS, U); tgl_insert_status_expire (TLS, U); } } } break; case CODE_user_status_offline: if (S->online != -1) { if (S->online) { tgl_insert_status_update (TLS, U); } if (S->online == 1) { tgl_remove_status_expire (TLS, U); } } S->online = -1; S->when = DS_LVAL (DS_US->was_online); break; case CODE_user_status_recently: if (S->online != -2) { if (S->online) { tgl_insert_status_update (TLS, U); } if (S->online == 1) { tgl_remove_status_expire (TLS, U); } } S->online = -2; break; case CODE_user_status_last_week: if (S->online != -3) { if (S->online) { tgl_insert_status_update (TLS, U); } if (S->online == 1) { tgl_remove_status_expire (TLS, U); } } S->online = -3; break; case CODE_user_status_last_month: if (S->online != -4) { if (S->online) { tgl_insert_status_update (TLS, U); } if (S->online == 1) { tgl_remove_status_expire (TLS, U); } } S->online = -4; break; default: assert (0); } return 0; } struct tgl_user *tglf_fetch_alloc_user (struct tgl_state *TLS, struct tl_ds_user *DS_U) { if (!DS_U) { return 0; } if (DS_U->magic == CODE_user_empty) { return 0; } tgl_peer_id_t user_id = TGL_MK_USER (DS_LVAL (DS_U->id)); user_id.access_hash = DS_LVAL (DS_U->access_hash); struct tgl_user *U = (struct tgl_user *)tgl_peer_get (TLS, user_id); if (!U) { TLS->users_allocated ++; U = talloc0 (sizeof (tgl_peer_t)); U->id = user_id; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, (tgl_peer_t *)U, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)U; } int flags = U->flags; if (DS_LVAL (DS_U->flags) & (1 << 10)) { bl_do_set_our_id (TLS, U->id); flags |= TGLUF_SELF; } else { flags &= ~TGLUF_SELF; } if (DS_LVAL (DS_U->flags) & (1 << 11)) { flags |= TGLUF_CONTACT; } else { flags &= ~TGLUF_CONTACT; } if (DS_LVAL (DS_U->flags) & (1 << 12)) { flags |= TGLUF_MUTUAL_CONTACT; } else { flags &= ~TGLUF_MUTUAL_CONTACT; } if (DS_LVAL (DS_U->flags) & (1 << 14)) { flags |= TGLUF_BOT; U->bot = 1; } else { flags &= ~TGLUF_BOT; U->bot = 0; } /* if (DS_LVAL (DS_U->flags) & (1 << 15)) { flags |= TGLUF_BOT_FULL_ACCESS; } if (DS_LVAL (DS_U->flags) & (1 << 16)) { flags |= TGLUF_BOT_NO_GROUPS; }*/ if (DS_LVAL (DS_U->flags) & (1 << 17)) { flags |= TGLUF_OFFICIAL; } else { flags &= ~TGLUF_OFFICIAL; } if (!(flags & TGLUF_CREATED)) { flags |= TGLUF_CREATE | TGLUF_CREATED; } bl_do_user (TLS, tgl_get_peer_id (U->id), DS_U->access_hash, DS_STR (DS_U->first_name), DS_STR (DS_U->last_name), DS_STR (DS_U->phone), DS_STR (DS_U->username), NULL, DS_U->photo, NULL, NULL, NULL, flags ); if (DS_U->status) { assert (tglf_fetch_user_status (TLS, &U->status, U, DS_U->status) >= 0); } if (DS_LVAL (DS_U->flags) & (1 << 13)) { if (!(U->flags & TGLUF_DELETED)) { bl_do_peer_delete (TLS, U->id); } } return U; } struct tgl_user *tglf_fetch_alloc_user_full (struct tgl_state *TLS, struct tl_ds_user_full *DS_UF) { if (!DS_UF) { return NULL; } struct tgl_user *U = tglf_fetch_alloc_user (TLS, DS_UF->user); if (!U) { return NULL; } int flags = U->flags; if (DS_BVAL (DS_UF->blocked)) { flags |= TGLUF_BLOCKED; } else { flags &= ~TGLUF_BLOCKED; } bl_do_user (TLS, tgl_get_peer_id (U->id), NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0, DS_UF->profile_photo, NULL, NULL, NULL, DS_UF->bot_info, flags ); return U; } void str_to_256 (unsigned char *dst, char *src, int src_len) { if (src_len >= 256) { memcpy (dst, src + src_len - 256, 256); } else { memset(dst, 0, 256 - src_len); memcpy (dst + 256 - src_len, src, src_len); } } void str_to_32 (unsigned char *dst, char *src, int src_len) { if (src_len >= 32) { memcpy (dst, src + src_len - 32, 32); } else { memset(dst, 0, 32 - src_len); memcpy (dst + 32 - src_len, src, src_len); } } struct tgl_secret_chat *tglf_fetch_alloc_encrypted_chat (struct tgl_state *TLS, struct tl_ds_encrypted_chat *DS_EC) { if (!DS_EC) { return NULL; } if (DS_EC->magic == CODE_encrypted_chat_empty) { return NULL; } tgl_peer_id_t chat_id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_EC->id)); chat_id.access_hash = DS_LVAL (DS_EC->access_hash); struct tgl_secret_chat *U = (void *)tgl_peer_get (TLS, chat_id); if (!U) { TLS->encr_chats_allocated ++; U = talloc0 (sizeof (tgl_peer_t)); U->id = chat_id; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, (tgl_peer_t *)U, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)U; } int new = !(U->flags & TGLPF_CREATED); if (DS_EC->magic == CODE_encrypted_chat_discarded) { if (new) { vlogprintf (E_WARNING, "Unknown chat in deleted state. May be we forgot something...\n"); return U; } bl_do_peer_delete (TLS, U->id); //write_secret_chat_file (); return U; } static unsigned char g_key[256]; if (new) { if (DS_EC->magic != CODE_encrypted_chat_requested) { vlogprintf (E_WARNING, "Unknown chat. May be we forgot something...\n"); return U; } str_to_256 (g_key, DS_STR (DS_EC->g_a)); int user_id = DS_LVAL (DS_EC->participant_id) + DS_LVAL (DS_EC->admin_id) - tgl_get_peer_id (TLS->our_id); int r = sc_request; bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), DS_EC->access_hash, DS_EC->date, DS_EC->admin_id, &user_id, NULL, (void *)g_key, NULL, &r, NULL, NULL, NULL, NULL, NULL, NULL, TGLECF_CREATE | TGLECF_CREATED, NULL, 0 ); } else { if (DS_EC->magic == CODE_encrypted_chat_waiting) { int r = sc_waiting; bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), DS_EC->access_hash, DS_EC->date, NULL, NULL, NULL, NULL, NULL, &r, NULL, NULL, NULL, NULL, NULL, NULL, TGL_FLAGS_UNCHANGED, NULL, 0 ); return U; // We needed only access hash from here } str_to_256 (g_key, DS_STR (DS_EC->g_a_or_b)); //write_secret_chat_file (); int r = sc_ok; bl_do_encr_chat (TLS, tgl_get_peer_id (U->id), DS_EC->access_hash, DS_EC->date, NULL, NULL, NULL, g_key, NULL, &r, NULL, NULL, NULL, NULL, NULL, DS_EC->key_fingerprint, TGL_FLAGS_UNCHANGED, NULL, 0 ); } return U; } struct tgl_chat *tglf_fetch_alloc_chat (struct tgl_state *TLS, struct tl_ds_chat *DS_C) { if (!DS_C) { return NULL; } if (DS_C->magic == CODE_chat_empty) { return NULL; } if (DS_C->magic == CODE_channel || DS_C->magic == CODE_channel_forbidden) { return (void *)tglf_fetch_alloc_channel (TLS, DS_C); } tgl_peer_id_t chat_id = TGL_MK_CHAT (DS_LVAL (DS_C->id)); chat_id.access_hash = 0; // chats don't have access hash struct tgl_chat *C = (void *)tgl_peer_get (TLS, chat_id); if (!C) { TLS->chats_allocated ++; C = talloc0 (sizeof (tgl_peer_t)); C->id = chat_id; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, (tgl_peer_t *)C, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)C; } C->id = chat_id; int flags = C->flags; if (!(flags & TGLCF_CREATED)) { flags |= TGLCF_CREATE | TGLCF_CREATED; } if (DS_LVAL (DS_C->flags) & 1) { flags |= TGLCF_CREATOR; } else { flags &= ~TGLCF_CREATOR; } if (DS_LVAL (DS_C->flags) & 2) { flags |= TGLCF_KICKED; } else { flags &= ~TGLCF_KICKED; } if (DS_LVAL (DS_C->flags) & 4) { flags |= TGLCF_LEFT; } else { flags &= ~TGLCF_LEFT; } if (DS_LVAL (DS_C->flags) & 8) { flags |= TGLCF_ADMINS_ENABLED; } else { flags &= ~TGLCF_ADMINS_ENABLED; } if (DS_LVAL (DS_C->flags) & 16) { flags |= TGLCF_ADMIN; } else { flags &= ~TGLCF_ADMIN; } if (DS_LVAL (DS_C->flags) & 32) { flags |= TGLCF_DEACTIVATED; } else { flags &= ~TGLCF_DEACTIVATED; } bl_do_chat (TLS, tgl_get_peer_id (C->id), DS_STR (DS_C->title), DS_C->participants_count, DS_C->date, NULL, NULL, DS_C->photo, NULL, NULL, NULL, NULL, flags ); return C; } struct tgl_chat *tglf_fetch_alloc_chat_full (struct tgl_state *TLS, struct tl_ds_messages_chat_full *DS_MCF) { if (!DS_MCF) { return NULL; } if (DS_MCF->full_chat->magic == CODE_channel_full) { return (void *)tglf_fetch_alloc_channel_full (TLS, DS_MCF); } if (DS_MCF->users) { int i; for (i = 0; i < DS_LVAL (DS_MCF->users->cnt); i++) { tglf_fetch_alloc_user (TLS, DS_MCF->users->data[i]); } } if (DS_MCF->chats) { int i; for (i = 0; i < DS_LVAL (DS_MCF->chats->cnt); i++) { tglf_fetch_alloc_chat (TLS, DS_MCF->chats->data[i]); } } struct tl_ds_chat_full *DS_CF = DS_MCF->full_chat; if (DS_CF->bot_info) { int n = DS_LVAL (DS_CF->bot_info->cnt); int i; for (i = 0; i < n; i++) { struct tl_ds_bot_info *DS_BI = DS_CF->bot_info->data[i]; tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_BI->user_id))); if (P && (P->flags & TGLCF_CREATED)) { bl_do_user (TLS, tgl_get_peer_id (P->id), NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, DS_BI, TGL_FLAGS_UNCHANGED ); } } } tgl_peer_id_t chat_id = TGL_MK_CHAT (DS_LVAL (DS_CF->id)); struct tgl_chat *C = (void *)tgl_peer_get (TLS, chat_id); assert (C); bl_do_chat (TLS, tgl_get_peer_id (C->id), NULL, 0, NULL, NULL, DS_CF->participants->version, (struct tl_ds_vector *)DS_CF->participants->participants, NULL, DS_CF->chat_photo, NULL, //DS_CF->participants->admin_id, NULL, NULL, C->flags & 0xffff ); return C; } struct tgl_channel *tglf_fetch_alloc_channel (struct tgl_state *TLS, struct tl_ds_chat *DS_C) { if (!DS_C) { return NULL; } tgl_peer_id_t chat_id = TGL_MK_CHANNEL (DS_LVAL (DS_C->id)); chat_id.access_hash = DS_LVAL (DS_C->access_hash); struct tgl_channel *C = (void *)tgl_peer_get (TLS, chat_id); if (!C) { TLS->channels_allocated ++; C = talloc0 (sizeof (tgl_peer_t)); C->id = chat_id; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, (tgl_peer_t *)C, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = (tgl_peer_t *)C; } C->id = chat_id; int flags = C->flags; if (!(flags & TGLCHF_CREATED)) { flags |= TGLCHF_CREATE | TGLCHF_CREATED; } if (DS_LVAL (DS_C->flags) & 1) { flags |= TGLCHF_CREATOR; } else { flags &= ~TGLCHF_CREATOR; } if (DS_LVAL (DS_C->flags) & 2) { flags |= TGLCHF_KICKED; } else { flags &= ~TGLCHF_KICKED; } if (DS_LVAL (DS_C->flags) & 4) { flags |= TGLCHF_LEFT; } else { flags &= ~TGLCHF_LEFT; } if (DS_LVAL (DS_C->flags) & 8) { flags |= TGLCHF_EDITOR; } else { flags &= ~TGLCHF_EDITOR; } if (DS_LVAL (DS_C->flags) & 16) { flags |= TGLCHF_MODERATOR; } else { flags &= ~TGLCHF_MODERATOR; } if (DS_LVAL (DS_C->flags) & 32) { flags |= TGLCHF_BROADCAST; } else { flags &= ~TGLCHF_BROADCAST; } if (DS_LVAL (DS_C->flags) & 128) { flags |= TGLCHF_OFFICIAL; } else { flags &= ~TGLCHF_OFFICIAL; } if (DS_LVAL (DS_C->flags) & 256) { flags |= TGLCHF_MEGAGROUP; } else { flags &= ~TGLCHF_MEGAGROUP; } bl_do_channel (TLS, tgl_get_peer_id (C->id), DS_C->access_hash, DS_C->date, DS_STR (DS_C->title), DS_STR (DS_C->username), DS_C->photo, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, flags ); return C; } struct tgl_channel *tglf_fetch_alloc_channel_full (struct tgl_state *TLS, struct tl_ds_messages_chat_full *DS_MCF) { if (!DS_MCF) { return NULL; } if (DS_MCF->users) { int i; for (i = 0; i < DS_LVAL (DS_MCF->users->cnt); i++) { tglf_fetch_alloc_user (TLS, DS_MCF->users->data[i]); } } if (DS_MCF->chats) { int i; for (i = 0; i < DS_LVAL (DS_MCF->chats->cnt); i++) { tglf_fetch_alloc_chat (TLS, DS_MCF->chats->data[i]); } } struct tl_ds_chat_full *DS_CF = DS_MCF->full_chat; tgl_peer_id_t chat_id = TGL_MK_CHANNEL (DS_LVAL (DS_CF->id)); struct tgl_channel *C = (void *)tgl_peer_get (TLS, chat_id); assert (C); bl_do_channel (TLS, tgl_get_peer_id (C->id), NULL, NULL, NULL, 0, NULL, 0, NULL, DS_CF->chat_photo, NULL, DS_STR (DS_CF->about), DS_CF->participants_count, DS_CF->admins_count, DS_CF->kicked_count, DS_CF->read_inbox_max_id, TGL_FLAGS_UNCHANGED ); return C; } void tglf_fetch_photo_size (struct tgl_state *TLS, struct tgl_photo_size *S, struct tl_ds_photo_size *DS_PS) { memset (S, 0, sizeof (*S)); S->type = DS_STR_DUP (DS_PS->type); S->w = DS_LVAL (DS_PS->w); S->h = DS_LVAL (DS_PS->h); S->size = DS_LVAL (DS_PS->size); if (DS_PS->bytes) { S->size = DS_PS->bytes->len; } tglf_fetch_file_location (TLS, &S->loc, DS_PS->location); } void tglf_fetch_geo (struct tgl_state *TLS, struct tgl_geo *G, struct tl_ds_geo_point *DS_GP) { G->longitude = DS_LVAL (DS_GP->longitude); G->latitude = DS_LVAL (DS_GP->latitude); } struct tgl_photo *tglf_fetch_alloc_photo (struct tgl_state *TLS, struct tl_ds_photo *DS_P) { if (!DS_P) { return NULL; } if (DS_P->magic == CODE_photo_empty) { return NULL; } struct tgl_photo *P = tgl_photo_get (TLS, DS_LVAL (DS_P->id)); if (P) { P->refcnt ++; return P; } P = talloc0 (sizeof (*P)); P->id = DS_LVAL (DS_P->id); P->refcnt = 1; tgl_photo_insert (TLS, P); P->access_hash = DS_LVAL (DS_P->access_hash); //P->user_id = DS_LVAL (DS_P->user_id); P->date = DS_LVAL (DS_P->date); P->caption = NULL;//DS_STR_DUP (DS_P->caption); /*if (DS_P->geo) { tglf_fetch_geo (TLS, &P->geo, DS_P->geo); }*/ P->sizes_num = DS_LVAL (DS_P->sizes->cnt); P->sizes = talloc (sizeof (struct tgl_photo_size) * P->sizes_num); int i; for (i = 0; i < P->sizes_num; i++) { tglf_fetch_photo_size (TLS, &P->sizes[i], DS_P->sizes->data[i]); } return P; } struct tgl_document *tglf_fetch_alloc_video (struct tgl_state *TLS, struct tl_ds_video *DS_V) { if (!DS_V) { return NULL; } if (DS_V->magic == CODE_video_empty) { return NULL; } struct tgl_document *D = tgl_document_get (TLS, DS_LVAL (DS_V->id)); if (D) { D->refcnt ++; return D; } D = talloc0 (sizeof (*D)); D->id = DS_LVAL (DS_V->id); D->refcnt = 1; tgl_document_insert (TLS, D); D->flags = TGLDF_VIDEO; D->access_hash = DS_LVAL (DS_V->access_hash); //D->user_id = DS_LVAL (DS_V->user_id); D->date = DS_LVAL (DS_V->date); D->caption = NULL;//DS_STR_DUP (DS_V->caption); D->duration = DS_LVAL (DS_V->duration); D->mime_type = tstrdup ("video/");//DS_STR_DUP (DS_V->mime_type); D->size = DS_LVAL (DS_V->size); tglf_fetch_photo_size (TLS, &D->thumb, DS_V->thumb); D->dc_id = DS_LVAL (DS_V->dc_id); D->w = DS_LVAL (DS_V->w); D->h = DS_LVAL (DS_V->h); return D; } struct tgl_document *tglf_fetch_alloc_audio (struct tgl_state *TLS, struct tl_ds_audio *DS_A) { if (!DS_A) { return NULL; } if (DS_A->magic == CODE_audio_empty) { return NULL; } struct tgl_document *D = tgl_document_get (TLS, DS_LVAL (DS_A->id)); if (D) { D->refcnt ++; return D; } D = talloc0 (sizeof (*D)); D->id = DS_LVAL (DS_A->id); D->refcnt = 1; tgl_document_insert (TLS, D); D->flags = TGLDF_AUDIO; D->access_hash = DS_LVAL (DS_A->access_hash); //D->user_id = DS_LVAL (DS_A->user_id); D->date = DS_LVAL (DS_A->date); D->duration = DS_LVAL (DS_A->duration); D->mime_type = DS_STR_DUP (DS_A->mime_type); D->size = DS_LVAL (DS_A->size); D->dc_id = DS_LVAL (DS_A->dc_id); return D; } void tglf_fetch_document_attribute (struct tgl_state *TLS, struct tgl_document *D, struct tl_ds_document_attribute *DS_DA) { switch (DS_DA->magic) { case CODE_document_attribute_image_size: D->flags |= TGLDF_IMAGE; D->w = DS_LVAL (DS_DA->w); D->h = DS_LVAL (DS_DA->h); return; case CODE_document_attribute_animated: D->flags |= TGLDF_ANIMATED; return; case CODE_document_attribute_sticker: D->flags |= TGLDF_STICKER; return; case CODE_document_attribute_video: D->flags |= TGLDF_VIDEO; D->duration = DS_LVAL (DS_DA->duration); D->w = DS_LVAL (DS_DA->w); D->h = DS_LVAL (DS_DA->h); return; case CODE_document_attribute_audio: D->flags |= TGLDF_AUDIO; D->duration = DS_LVAL (DS_DA->duration); return; case CODE_document_attribute_filename: D->caption = DS_STR_DUP (DS_DA->file_name); return; default: assert (0); } } struct tgl_document *tglf_fetch_alloc_document (struct tgl_state *TLS, struct tl_ds_document *DS_D) { if (!DS_D) { return NULL; } if (DS_D->magic == CODE_document_empty) { return NULL; } struct tgl_document *D = tgl_document_get (TLS, DS_LVAL (DS_D->id)); if (D) { D->refcnt ++; return D; } D = talloc0 (sizeof (*D)); D->id = DS_LVAL (DS_D->id); D->refcnt = 1; tgl_document_insert (TLS, D); D->access_hash = DS_LVAL (DS_D->access_hash); //D->user_id = DS_LVAL (DS_D->user_id); D->date = DS_LVAL (DS_D->date); //D->caption = DS_STR_DUP (DS_D->file_name); D->mime_type = DS_STR_DUP (DS_D->mime_type); D->size = DS_LVAL (DS_D->size); D->dc_id = DS_LVAL (DS_D->dc_id); tglf_fetch_photo_size (TLS, &D->thumb, DS_D->thumb); if (DS_D->attributes) { int i; for (i = 0; i < DS_LVAL (DS_D->attributes->cnt); i++) { tglf_fetch_document_attribute (TLS, D, DS_D->attributes->data[i]); } } return D; } struct tgl_webpage *tglf_fetch_alloc_webpage (struct tgl_state *TLS, struct tl_ds_web_page *DS_W) { if (!DS_W) { return NULL; } struct tgl_webpage *W = tgl_webpage_get (TLS, DS_LVAL (DS_W->id)); if (W) { W->refcnt ++; } else { W = talloc0 (sizeof (*W)); W->id = DS_LVAL (DS_W->id); W->refcnt = 1; tgl_webpage_insert (TLS, W); } if (!W->url) { W->url = DS_STR_DUP (DS_W->url); } if (!W->display_url) { W->display_url = DS_STR_DUP (DS_W->display_url); } if (!W->type) { W->type = DS_STR_DUP (DS_W->type); } if (!W->site_name) { W->site_name = DS_STR_DUP (DS_W->site_name); } if (!W->title) { W->title = DS_STR_DUP (DS_W->title); } if (!W->photo) { W->photo = tglf_fetch_alloc_photo (TLS, DS_W->photo); } if (!W->description) { W->description = DS_STR_DUP (DS_W->description); } if (!W->embed_url) { W->embed_url = DS_STR_DUP (DS_W->embed_url); } if (!W->embed_type) { W->embed_type = DS_STR_DUP (DS_W->embed_type); } W->embed_width = DS_LVAL (DS_W->embed_width); W->embed_height = DS_LVAL (DS_W->embed_height); W->duration = DS_LVAL (DS_W->duration); if (!W->author) { W->author = DS_STR_DUP (DS_W->author); } return W; } void tglf_fetch_message_action (struct tgl_state *TLS, struct tgl_message_action *M, struct tl_ds_message_action *DS_MA) { if (!DS_MA) { return; } memset (M, 0, sizeof (*M)); switch (DS_MA->magic) { case CODE_message_action_empty: M->type = tgl_message_action_none; break; /*case CODE_message_action_geo_chat_create: { M->type = tgl_message_action_geo_chat_create; assert (0); } break;*/ /*case CODE_message_action_geo_chat_checkin: M->type = tgl_message_action_geo_chat_checkin; break;*/ case CODE_message_action_chat_create: { M->type = tgl_message_action_chat_create; M->title = DS_STR_DUP (DS_MA->title); M->user_num = DS_LVAL (DS_MA->users->cnt); M->users = talloc (M->user_num * 4); int i; for (i = 0; i < M->user_num; i++) { M->users[i] = DS_LVAL (DS_MA->users->data[i]); } } break; case CODE_message_action_chat_edit_title: M->type = tgl_message_action_chat_edit_title; M->new_title = DS_STR_DUP (DS_MA->title); break; case CODE_message_action_chat_edit_photo: M->type = tgl_message_action_chat_edit_photo; M->photo = tglf_fetch_alloc_photo (TLS, DS_MA->photo); break; case CODE_message_action_chat_delete_photo: M->type = tgl_message_action_chat_delete_photo; break; case CODE_message_action_chat_add_user: M->type = tgl_message_action_chat_add_users; M->user_num = DS_LVAL (DS_MA->users->cnt); M->users = talloc (4 * M->user_num); { int i; for (i = 0; i < M->user_num; i++) { M->users[i] = DS_LVAL (DS_MA->users->data[i]); } } break; case CODE_message_action_chat_delete_user: M->type = tgl_message_action_chat_delete_user; M->user = DS_LVAL (DS_MA->user_id); break; case CODE_message_action_chat_joined_by_link: M->type = tgl_message_action_chat_add_user_by_link; M->user = DS_LVAL (DS_MA->inviter_id); break; case CODE_message_action_channel_create: M->type = tgl_message_action_channel_create; M->title = DS_STR_DUP (DS_MA->title); break; case CODE_message_action_chat_migrate_to: M->type = tgl_message_action_migrated_to; M->channel_id = DS_LVAL (DS_MA->channel_id); break; case CODE_message_action_channel_migrate_from: M->type = tgl_message_action_migrated_from; M->title = DS_STR_DUP (DS_MA->title); break; default: assert (0); } } struct tgl_message *tglf_fetch_alloc_message_short (struct tgl_state *TLS, struct tl_ds_updates *DS_U) { tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_U->user_id))); if (!P || !(P->flags & TGLPF_CREATED)) { tgl_do_get_difference (TLS, 0, 0, 0); return NULL; } tgl_message_id_t msg_id = tgl_peer_id_to_msg_id (P->id, DS_LVAL (DS_U->id)); struct tgl_message *M = tgl_message_get (TLS, &msg_id); if (!M) { M = talloc0 (sizeof (*M)); M->permanent_id = msg_id; tglm_message_insert_tree (TLS, M); TLS->messages_allocated ++; assert (tgl_message_get (TLS, &msg_id) == M); } int flags = M->flags & 0xffff; if (M->flags & TGLMF_PENDING) { M->flags ^= TGLMF_PENDING; } if (!(flags & TGLMF_CREATED)) { flags |= TGLMF_CREATE | TGLMF_CREATED; } int f = DS_LVAL (DS_U->flags); if (f & 1) { flags |= TGLMF_UNREAD; } if (f & 2) { flags |= TGLMF_OUT; } if (f & 16) { flags |= TGLMF_MENTION; } struct tl_ds_message_media A; A.magic = CODE_message_media_empty; tgl_peer_id_t our_id = TLS->our_id; tgl_peer_id_t peer_id = P->id; tgl_peer_id_t fwd_from_id; if (DS_U->fwd_from_id) { fwd_from_id = tglf_fetch_peer_id (TLS, DS_U->fwd_from_id); } else { fwd_from_id = TGL_MK_USER (0); } bl_do_edit_message (TLS, &msg_id, (f & 2) ? &our_id : &peer_id, (f & 2) ? &peer_id : &our_id, DS_U->fwd_from_id ? &fwd_from_id : NULL, DS_U->fwd_date, DS_U->date, DS_STR (DS_U->message), &A, NULL, DS_U->reply_to_msg_id, NULL, (void *)DS_U->entities, flags ); return M; } struct tgl_message *tglf_fetch_alloc_message_short_chat (struct tgl_state *TLS, struct tl_ds_updates *DS_U) { tgl_peer_t *F = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_U->from_id))); if (!F || !(F->flags & TGLPF_CREATED)) { tgl_do_get_difference (TLS, 0, 0, 0); return NULL; } tgl_peer_t *T = tgl_peer_get (TLS, TGL_MK_CHAT (DS_LVAL (DS_U->chat_id))); if (!T || !(T->flags & TGLPF_CREATED)) { tgl_do_get_difference (TLS, 0, 0, 0); return NULL; } tgl_message_id_t msg_id = tgl_peer_id_to_msg_id (T->id, DS_LVAL (DS_U->id)); struct tgl_message *M = tgl_message_get (TLS, &msg_id); if (!M) { M = talloc0 (sizeof (*M)); M->permanent_id = msg_id; tglm_message_insert_tree (TLS, M); TLS->messages_allocated ++; } int flags = M->flags & 0xffff; if (M->flags & TGLMF_PENDING) { M->flags ^= TGLMF_PENDING; } if (!(flags & TGLMF_CREATED)) { flags |= TGLMF_CREATE | TGLMF_CREATED; } int f = DS_LVAL (DS_U->flags); if (f & 1) { flags |= TGLMF_UNREAD; } if (f & 2) { flags |= TGLMF_OUT; } if (f & 16) { flags |= TGLMF_MENTION; } struct tl_ds_message_media A; A.magic = CODE_message_media_empty; tgl_peer_id_t from_id = F->id; tgl_peer_id_t to_id = T->id; tgl_peer_id_t fwd_from_id; if (DS_U->fwd_from_id) { fwd_from_id = tglf_fetch_peer_id (TLS, DS_U->fwd_from_id); } else { fwd_from_id = TGL_MK_USER (0); } bl_do_edit_message (TLS, &msg_id, &from_id, &to_id, DS_U->fwd_from_id ? &fwd_from_id : NULL, DS_U->fwd_date, DS_U->date, DS_STR (DS_U->message), &A, NULL, DS_U->reply_to_msg_id, NULL, NULL, flags ); return M; } void tglf_fetch_message_media (struct tgl_state *TLS, struct tgl_message_media *M, struct tl_ds_message_media *DS_MM) { if (!DS_MM) { return; } memset (M, 0, sizeof (*M)); switch (DS_MM->magic) { case CODE_message_media_empty: M->type = tgl_message_media_none; break; case CODE_message_media_photo: case CODE_message_media_photo_l27: M->type = tgl_message_media_photo; M->photo = tglf_fetch_alloc_photo (TLS, DS_MM->photo); M->caption = DS_STR_DUP (DS_MM->caption); break; case CODE_message_media_video: case CODE_message_media_video_l27: M->type = tgl_message_media_video; M->document = tglf_fetch_alloc_video (TLS, DS_MM->video); M->caption = DS_STR_DUP (DS_MM->caption); break; case CODE_message_media_audio: M->type = tgl_message_media_audio; M->document = tglf_fetch_alloc_audio (TLS, DS_MM->audio); M->caption = DS_STR_DUP (DS_MM->caption); break; case CODE_message_media_document: M->type = tgl_message_media_document; M->document = tglf_fetch_alloc_document (TLS, DS_MM->document); M->caption = DS_STR_DUP (DS_MM->caption); break; case CODE_message_media_geo: M->type = tgl_message_media_geo; tglf_fetch_geo (TLS, &M->geo, DS_MM->geo); break; case CODE_message_media_contact: M->type = tgl_message_media_contact; M->phone = DS_STR_DUP (DS_MM->phone_number); M->first_name = DS_STR_DUP (DS_MM->first_name); M->last_name = DS_STR_DUP (DS_MM->last_name); M->user_id = DS_LVAL (DS_MM->user_id); break; case CODE_message_media_web_page: M->type = tgl_message_media_webpage; M->webpage = tglf_fetch_alloc_webpage (TLS, DS_MM->webpage); break; case CODE_message_media_venue: M->type = tgl_message_media_venue; tglf_fetch_geo (TLS, &M->venue.geo, DS_MM->geo); M->venue.title = DS_STR_DUP (DS_MM->title); M->venue.address = DS_STR_DUP (DS_MM->address); M->venue.provider = DS_STR_DUP (DS_MM->provider); M->venue.venue_id = DS_STR_DUP (DS_MM->venue_id); break; case CODE_message_media_unsupported: M->type = tgl_message_media_unsupported; break; default: assert (0); } } void tglf_fetch_message_media_encrypted (struct tgl_state *TLS, struct tgl_message_media *M, struct tl_ds_decrypted_message_media *DS_DMM) { if (!DS_DMM) { return; } memset (M, 0, sizeof (*M)); switch (DS_DMM->magic) { case CODE_decrypted_message_media_empty: M->type = tgl_message_media_none; //M->type = CODE_message_media_empty; break; case CODE_decrypted_message_media_photo: case CODE_decrypted_message_media_video: case CODE_decrypted_message_media_video_l12: case CODE_decrypted_message_media_document: case CODE_decrypted_message_media_audio: //M->type = CODE_decrypted_message_media_video; M->type = tgl_message_media_document_encr; M->encr_document = talloc0 (sizeof (*M->encr_document)); switch (DS_DMM->magic) { case CODE_decrypted_message_media_photo: M->encr_document->flags = TGLDF_IMAGE; break; case CODE_decrypted_message_media_video: case CODE_decrypted_message_media_video_l12: M->encr_document->flags = TGLDF_VIDEO; break; case CODE_decrypted_message_media_document: //M->encr_document->flags = TGLDF_DOCUMENT; break; case CODE_decrypted_message_media_audio: M->encr_document->flags = TGLDF_AUDIO; break; } M->encr_document->w = DS_LVAL (DS_DMM->w); M->encr_document->h = DS_LVAL (DS_DMM->h); M->encr_document->size = DS_LVAL (DS_DMM->size); M->encr_document->duration = DS_LVAL (DS_DMM->duration); M->encr_document->mime_type = DS_STR_DUP (DS_DMM->mime_type); M->encr_document->key = talloc (32); str_to_32 (M->encr_document->key, DS_STR (DS_DMM->key)); M->encr_document->iv = talloc (32); str_to_32 (M->encr_document->iv, DS_STR (DS_DMM->iv)); break; case CODE_decrypted_message_media_geo_point: M->type = tgl_message_media_geo; M->geo.latitude = DS_LVAL (DS_DMM->latitude); M->geo.longitude = DS_LVAL (DS_DMM->longitude); break; case CODE_decrypted_message_media_contact: M->type = tgl_message_media_contact; M->phone = DS_STR_DUP (DS_DMM->phone_number); M->first_name = DS_STR_DUP (DS_DMM->first_name); M->last_name = DS_STR_DUP (DS_DMM->last_name); M->user_id = DS_LVAL (DS_DMM->user_id); break; default: assert (0); } } void tglf_fetch_message_action_encrypted (struct tgl_state *TLS, struct tgl_message_action *M, struct tl_ds_decrypted_message_action *DS_DMA) { if (!DS_DMA) { return; } switch (DS_DMA->magic) { case CODE_decrypted_message_action_set_message_t_t_l: M->type = tgl_message_action_set_message_ttl; M->ttl = DS_LVAL (DS_DMA->ttl_seconds); break; case CODE_decrypted_message_action_read_messages: M->type = tgl_message_action_read_messages; { M->read_cnt = DS_LVAL (DS_DMA->random_ids->cnt); int i; for (i = 0; i < M->read_cnt; i++) { tgl_message_id_t id; id.peer_type = TGL_PEER_RANDOM_ID; id.id = DS_LVAL (DS_DMA->random_ids->data[i]); struct tgl_message *N = tgl_message_get (TLS, &id); if (N) { N->flags &= ~TGLMF_UNREAD; } } } break; case CODE_decrypted_message_action_delete_messages: M->type = tgl_message_action_delete_messages; break; case CODE_decrypted_message_action_screenshot_messages: M->type = tgl_message_action_screenshot_messages; { M->screenshot_cnt = DS_LVAL (DS_DMA->random_ids->cnt); } break; case CODE_decrypted_message_action_notify_layer: M->type = tgl_message_action_notify_layer; M->layer = DS_LVAL (DS_DMA->layer); break; case CODE_decrypted_message_action_flush_history: M->type = tgl_message_action_flush_history; break; case CODE_decrypted_message_action_typing: M->type = tgl_message_action_typing; M->typing = tglf_fetch_typing (DS_DMA->action); break; case CODE_decrypted_message_action_resend: M->type = tgl_message_action_resend; M->start_seq_no = DS_LVAL (DS_DMA->start_seq_no); M->end_seq_no = DS_LVAL (DS_DMA->end_seq_no); break; case CODE_decrypted_message_action_noop: M->type = tgl_message_action_noop; break; case CODE_decrypted_message_action_request_key: M->type = tgl_message_action_request_key; M->exchange_id = DS_LVAL (DS_DMA->exchange_id); M->g_a = talloc (256); str_to_256 (M->g_a, DS_STR (DS_DMA->g_a)); break; case CODE_decrypted_message_action_accept_key: M->type = tgl_message_action_accept_key; M->exchange_id = DS_LVAL (DS_DMA->exchange_id); M->g_a = talloc (256); str_to_256 (M->g_a, DS_STR (DS_DMA->g_b)); M->key_fingerprint = DS_LVAL (DS_DMA->key_fingerprint); break; case CODE_decrypted_message_action_commit_key: M->type = tgl_message_action_commit_key; M->exchange_id = DS_LVAL (DS_DMA->exchange_id); M->key_fingerprint = DS_LVAL (DS_DMA->key_fingerprint); break; case CODE_decrypted_message_action_abort_key: M->type = tgl_message_action_abort_key; M->exchange_id = DS_LVAL (DS_DMA->exchange_id); break; default: assert (0); } } void tglf_fetch_message_entity (struct tgl_state *TLS, struct tgl_message_entity *E, struct tl_ds_message_entity *DS_ME) { E->start = DS_LVAL (DS_ME->offset); E->length = DS_LVAL (DS_ME->length); switch (DS_ME->magic) { case CODE_message_entity_unknown: E->type = tgl_message_entity_unknown; break; case CODE_message_entity_mention: E->type = tgl_message_entity_mention; break; case CODE_message_entity_hashtag: E->type = tgl_message_entity_hashtag; break; case CODE_message_entity_bot_command: E->type = tgl_message_entity_bot_command; break; case CODE_message_entity_url: E->type = tgl_message_entity_url; break; case CODE_message_entity_email: E->type = tgl_message_entity_email; break; case CODE_message_entity_bold: E->type = tgl_message_entity_bold; break; case CODE_message_entity_italic: E->type = tgl_message_entity_italic; break; case CODE_message_entity_code: E->type = tgl_message_entity_code; break; case CODE_message_entity_pre: E->type = tgl_message_entity_pre; break; case CODE_message_entity_text_url: E->type = tgl_message_entity_text_url; E->extra = DS_STR_DUP (DS_ME->url); break; default: assert (0); } } void tglf_fetch_message_entities (struct tgl_state *TLS, struct tgl_message *M, struct tl_ds_vector *DS) { M->entities_num = DS_LVAL (DS->f1); M->entities = talloc0 (M->entities_num * sizeof (struct tgl_message_entity)); int i; for (i = 0; i < M->entities_num; i++) { struct tl_ds_message_entity *D = DS->f2[i]; tglf_fetch_message_entity (TLS, &M->entities[i], D); } } struct tgl_message *tglf_fetch_alloc_message (struct tgl_state *TLS, struct tl_ds_message *DS_M, int *new_msg) { if (new_msg) { *new_msg = 0; } //assert (0); if (!DS_M || DS_M->magic == CODE_message_empty) { vlogprintf (E_NOTICE, "empty message\n"); return NULL; } tgl_peer_id_t to_id = tglf_fetch_peer_id (TLS, DS_M->to_id); tgl_peer_t *T = tgl_peer_get (TLS, to_id); if (!T || !(T->flags & TGLPF_CREATED)) { tgl_do_get_difference (TLS, 0, 0, 0); vlogprintf (E_NOTICE, "unknown to_id\n"); return NULL; } tgl_peer_t *P = T; tgl_peer_t *F = NULL; if (DS_M->from_id) { F = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_M->from_id))); if (!F || !(F->flags & TGLPF_CREATED)) { tgl_do_get_difference (TLS, 0, 0, 0); vlogprintf (E_NOTICE, "unknown from_id %d\n", DS_LVAL (DS_M->from_id)); return NULL; } if (!tgl_cmp_peer_id (to_id, TLS->our_id)) { P = F; } } tgl_peer_t *FF = NULL; if (DS_M->fwd_from_id) { tgl_peer_id_t FF_id = tglf_fetch_peer_id (TLS, DS_M->fwd_from_id); FF = tgl_peer_get (TLS, FF_id); if (!FF) { tgl_do_get_difference (TLS, 0, 0, 0); vlogprintf (E_NOTICE, "unknown fwd_id\n"); return NULL; } } tgl_message_id_t msg_id = tgl_peer_id_to_msg_id (P->id, DS_LVAL (DS_M->id)); struct tgl_message *M = tgl_message_get (TLS, &msg_id); if (!M) { M = talloc0 (sizeof (*M)); M->permanent_id = msg_id; tglm_message_insert_tree (TLS, M); TLS->messages_allocated ++; assert (tgl_message_get (TLS, &msg_id) == M); } int new = !(M->flags & TGLMF_CREATED); if (new_msg) { *new_msg = new; } if (new) { int flags = 0; if (DS_LVAL (DS_M->flags) & 1) { flags |= TGLMF_UNREAD; } if (DS_LVAL (DS_M->flags) & 2) { flags |= TGLMF_OUT; } if (DS_LVAL (DS_M->flags) & 16) { flags |= TGLMF_MENTION; } tgl_peer_id_t from_id; if (DS_M->from_id) { from_id = F->id; } else { from_id = TGL_MK_USER (0); } tgl_peer_id_t to_id = T->id; tgl_peer_id_t fwd_from_id; if (DS_M->fwd_from_id) { fwd_from_id = FF->id; } else { fwd_from_id = TGL_MK_USER (0); } bl_do_edit_message (TLS, &msg_id, DS_M->from_id ? &from_id : NULL, &to_id, DS_M->fwd_from_id ? &fwd_from_id : NULL, DS_M->fwd_date, DS_M->date, DS_STR (DS_M->message), DS_M->media, DS_M->action, DS_M->reply_to_msg_id, DS_M->reply_markup, (void *)DS_M->entities, flags | TGLMF_CREATE | TGLMF_CREATED ); } return M; } static int *decr_ptr; static int *decr_end; static int decrypt_encrypted_message (struct tgl_secret_chat *E) { int *msg_key = decr_ptr; decr_ptr += 4; assert (decr_ptr < decr_end); static unsigned char sha1a_buffer[20]; static unsigned char sha1b_buffer[20]; static unsigned char sha1c_buffer[20]; static unsigned char sha1d_buffer[20]; static unsigned char buf[64]; int *e_key = E->exchange_state != tgl_sce_committed ? E->key : E->exchange_key; memcpy (buf, msg_key, 16); memcpy (buf + 16, e_key, 32); TGLC_sha1 (buf, 48, sha1a_buffer); memcpy (buf, e_key + 8, 16); memcpy (buf + 16, msg_key, 16); memcpy (buf + 32, e_key + 12, 16); TGLC_sha1 (buf, 48, sha1b_buffer); memcpy (buf, e_key + 16, 32); memcpy (buf + 32, msg_key, 16); TGLC_sha1 (buf, 48, sha1c_buffer); memcpy (buf, msg_key, 16); memcpy (buf + 16, e_key + 24, 32); TGLC_sha1 (buf, 48, sha1d_buffer); static unsigned char key[32]; memcpy (key, sha1a_buffer + 0, 8); memcpy (key + 8, sha1b_buffer + 8, 12); memcpy (key + 20, sha1c_buffer + 4, 12); static unsigned char iv[32]; memcpy (iv, sha1a_buffer + 8, 12); memcpy (iv + 12, sha1b_buffer + 0, 8); memcpy (iv + 20, sha1c_buffer + 16, 4); memcpy (iv + 24, sha1d_buffer + 0, 8); TGLC_aes_key aes_key; TGLC_aes_set_decrypt_key (key, 256, &aes_key); TGLC_aes_ige_encrypt ((void *)decr_ptr, (void *)decr_ptr, 4 * (decr_end - decr_ptr), &aes_key, iv, 0); memset (&aes_key, 0, sizeof (aes_key)); int x = *(decr_ptr); if (x < 0 || (x & 3)) { return -1; } assert (x >= 0 && !(x & 3)); TGLC_sha1 ((void *)decr_ptr, 4 + x, sha1a_buffer); if (memcmp (sha1a_buffer + 4, msg_key, 16)) { return -1; } return 0; } struct tgl_message *tglf_fetch_encrypted_message (struct tgl_state *TLS, struct tl_ds_encrypted_message *DS_EM) { if (!DS_EM) { return NULL; } tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_ENCR_CHAT (DS_LVAL (DS_EM->chat_id))); if (!P || P->encr_chat.state != sc_ok) { vlogprintf (E_WARNING, "Encrypted message to unknown chat. Dropping\n"); return NULL; } tgl_message_id_t msg_id = tgl_peer_id_to_msg_id (P->id, DS_LVAL (DS_EM->random_id)); struct tgl_message *M = tgl_message_get (TLS, &msg_id); if (!M) { M = talloc0 (sizeof (*M)); M->permanent_id = msg_id; tglm_message_insert_tree (TLS, M); TLS->messages_allocated ++; assert (tgl_message_get (TLS, &msg_id) == M); } int new = !(M->flags & TGLMF_CREATED); if (!new) { return M; } decr_ptr = (void *)DS_EM->bytes->data; decr_end = decr_ptr + (DS_EM->bytes->len / 4); if (P->encr_chat.exchange_state == tgl_sce_committed && P->encr_chat.key_fingerprint == *(long long *)decr_ptr) { tgl_do_confirm_exchange (TLS, (void *)P, 0); assert (P->encr_chat.exchange_state == tgl_sce_none); } long long key_fingerprint = P->encr_chat.exchange_state != tgl_sce_committed ? P->encr_chat.key_fingerprint : P->encr_chat.exchange_key_fingerprint; if (*(long long *)decr_ptr != key_fingerprint) { vlogprintf (E_WARNING, "Encrypted message with bad fingerprint to chat %s\n", P->print_name); return M; } decr_ptr += 2; if (decrypt_encrypted_message (&P->encr_chat) < 0) { vlogprintf (E_WARNING, "can not decrypt message\n"); return M; } int *save_in_ptr = in_ptr; int *save_in_end = in_end; in_ptr = decr_ptr; int ll = *in_ptr; in_end = in_ptr + ll / 4 + 1; assert (fetch_int () == ll); if (skip_type_decrypted_message_layer (TYPE_TO_PARAM (decrypted_message_layer)) < 0 || in_ptr != in_end) { vlogprintf (E_WARNING, "can not fetch message\n"); in_ptr = save_in_ptr; in_end = save_in_end; return M; } in_ptr = decr_ptr; assert (fetch_int () == ll); struct tl_ds_decrypted_message_layer *DS_DML = fetch_ds_type_decrypted_message_layer (TYPE_TO_PARAM (decrypted_message_layer)); assert (DS_DML); in_ptr = save_in_ptr; in_end = save_in_end; //bl_do_encr_chat_set_layer (TLS, (void *)P, DS_LVAL (DS_DML->layer)); bl_do_encr_chat (TLS, tgl_get_peer_id (P->id), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DS_DML->layer, NULL, NULL, NULL, NULL, TGL_FLAGS_UNCHANGED, NULL, 0 ); int in_seq_no = DS_LVAL (DS_DML->out_seq_no); int out_seq_no = DS_LVAL (DS_DML->in_seq_no); if (in_seq_no / 2 != P->encr_chat.in_seq_no) { vlogprintf (E_WARNING, "Hole in seq in secret chat. in_seq_no = %d, expect_seq_no = %d\n", in_seq_no / 2, P->encr_chat.in_seq_no); free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } if ((in_seq_no & 1) != 1 - (P->encr_chat.admin_id == tgl_get_peer_id (TLS->our_id)) || (out_seq_no & 1) != (P->encr_chat.admin_id == tgl_get_peer_id (TLS->our_id))) { vlogprintf (E_WARNING, "Bad msg admin\n"); free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } if (out_seq_no / 2 > P->encr_chat.out_seq_no) { vlogprintf (E_WARNING, "In seq no is bigger than our's out seq no (out_seq_no = %d, our_out_seq_no = %d). Drop\n", out_seq_no / 2, P->encr_chat.out_seq_no); free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } if (out_seq_no / 2 < P->encr_chat.last_in_seq_no) { vlogprintf (E_WARNING, "Clients in_seq_no decreased (out_seq_no = %d, last_out_seq_no = %d). Drop\n", out_seq_no / 2, P->encr_chat.last_in_seq_no); free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } struct tl_ds_decrypted_message *DS_DM = DS_DML->message; if (M->permanent_id.id != DS_LVAL (DS_DM->random_id)) { vlogprintf (E_ERROR, "Incorrect message: id = %" INT64_PRINTF_MODIFIER "d, new_id = %" INT64_PRINTF_MODIFIER "d\n", M->permanent_id.id, DS_LVAL (DS_DM->random_id)); free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } tgl_peer_id_t from_id = TGL_MK_USER (P->encr_chat.user_id); bl_do_edit_message_encr (TLS, &M->permanent_id, &from_id, &P->id, DS_EM->date, DS_STR (DS_DM->message), DS_DM->media, DS_DM->action, DS_EM->file, TGLMF_CREATE | TGLMF_CREATED | TGLMF_ENCRYPTED); if (in_seq_no >= 0 && out_seq_no >= 0) { //bl_do_encr_chat_update_seq (TLS, (void *)P, in_seq_no / 2 + 1, out_seq_no / 2); in_seq_no = in_seq_no / 2 + 1; out_seq_no = out_seq_no / 2; bl_do_encr_chat (TLS, tgl_get_peer_id (P->id), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &in_seq_no, &out_seq_no, NULL, NULL, TGL_FLAGS_UNCHANGED, NULL, 0 ); assert (P->encr_chat.in_seq_no == in_seq_no); } free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer)); return M; } void tglf_fetch_encrypted_message_file (struct tgl_state *TLS, struct tgl_message_media *M, struct tl_ds_encrypted_file *DS_EF) { if (DS_EF->magic == CODE_encrypted_file_empty) { assert (M->type != tgl_message_media_document_encr); } else { assert (M->type == tgl_message_media_document_encr); assert (M->encr_document); M->encr_document->id = DS_LVAL (DS_EF->id); M->encr_document->access_hash = DS_LVAL (DS_EF->access_hash); if (!M->encr_document->size) { M->encr_document->size = DS_LVAL (DS_EF->size); } M->encr_document->dc_id = DS_LVAL (DS_EF->dc_id); M->encr_document->key_fingerprint = DS_LVAL (DS_EF->key_fingerprint); } } static int id_cmp (struct tgl_message *M1, struct tgl_message *M2) { if (M1->permanent_id.peer_type < M2->permanent_id.peer_type) { return -1; } if (M1->permanent_id.peer_type > M2->permanent_id.peer_type) { return 1; } if (M1->permanent_id.peer_id < M2->permanent_id.peer_id) { return -1; } if (M1->permanent_id.peer_id > M2->permanent_id.peer_id) { return 1; } if (M1->permanent_id.id < M2->permanent_id.id) { return -1; } if (M1->permanent_id.id > M2->permanent_id.id) { return 1; } else { return 0; } } static void increase_peer_size (struct tgl_state *TLS) { if (TLS->peer_num == TLS->peer_size) { int new_size = TLS->peer_size ? 2 * TLS->peer_size : 10; int old_size = TLS->peer_size; if (old_size) { TLS->Peers = trealloc (TLS->Peers, old_size * sizeof (void *), new_size * sizeof (void *)); } else { TLS->Peers = talloc (new_size * sizeof (void *)); } TLS->peer_size = new_size; } } struct tgl_message *tglf_fetch_alloc_encrypted_message (struct tgl_state *TLS, struct tl_ds_encrypted_message *DS_EM) { struct tgl_message *M = tglf_fetch_encrypted_message (TLS, DS_EM); if (!M) { return M; } if (M->flags & TGLMF_CREATED) { tgl_peer_t *_E = tgl_peer_get (TLS, M->to_id); assert (_E); struct tgl_secret_chat *E = &_E->encr_chat; if (M->action.type == tgl_message_action_request_key) { if (E->exchange_state == tgl_sce_none || (E->exchange_state == tgl_sce_requested && E->exchange_id > M->action.exchange_id )) { tgl_do_accept_exchange (TLS, E, M->action.exchange_id, M->action.g_a); } else { vlogprintf (E_WARNING, "Exchange: Incorrect state (received request, state = %d)\n", E->exchange_state); } } if (M->action.type == tgl_message_action_accept_key) { if (E->exchange_state == tgl_sce_requested && E->exchange_id == M->action.exchange_id) { tgl_do_commit_exchange (TLS, E, M->action.g_a); } else { vlogprintf (E_WARNING, "Exchange: Incorrect state (received accept, state = %d)\n", E->exchange_state); } } if (M->action.type == tgl_message_action_commit_key) { if (E->exchange_state == tgl_sce_accepted && E->exchange_id == M->action.exchange_id) { tgl_do_confirm_exchange (TLS, E, 1); } else { vlogprintf (E_WARNING, "Exchange: Incorrect state (received commit, state = %d)\n", E->exchange_state); } } if (M->action.type == tgl_message_action_abort_key) { if (E->exchange_state != tgl_sce_none && E->exchange_id == M->action.exchange_id) { tgl_do_abort_exchange (TLS, E); } else { vlogprintf (E_WARNING, "Exchange: Incorrect state (received abort, state = %d)\n", E->exchange_state); } } if (M->action.type == tgl_message_action_notify_layer) { bl_do_encr_chat (TLS, tgl_get_peer_id (E->id), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &M->action.layer, NULL, NULL, NULL, NULL, TGL_FLAGS_UNCHANGED, NULL, 0 ); } if (M->action.type == tgl_message_action_set_message_ttl) { //bl_do_encr_chat_set_ttl (TLS, E, M->action.ttl); bl_do_encr_chat (TLS, tgl_get_peer_id (E->id), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &M->action.ttl, NULL, NULL, NULL, NULL, NULL, TGL_FLAGS_UNCHANGED, NULL, 0 ); } } return M; } struct tgl_bot_info *tglf_fetch_alloc_bot_info (struct tgl_state *TLS, struct tl_ds_bot_info *DS_BI) { if (!DS_BI || DS_BI->magic == CODE_bot_info_empty) { return NULL; } struct tgl_bot_info *B = talloc (sizeof (*B)); B->version = DS_LVAL (DS_BI->version); B->share_text = DS_STR_DUP (DS_BI->share_text); B->description = DS_STR_DUP (DS_BI->description); B->commands_num = DS_LVAL (DS_BI->commands->cnt); B->commands = talloc (sizeof (struct tgl_bot_command) * B->commands_num); int i; for (i = 0; i < B->commands_num; i++) { struct tl_ds_bot_command *BC = DS_BI->commands->data[i]; B->commands[i].command = DS_STR_DUP (BC->command); B->commands[i].description = DS_STR_DUP (BC->description); } return B; } struct tgl_message_reply_markup *tglf_fetch_alloc_reply_markup (struct tgl_state *TLS, struct tgl_message *M, struct tl_ds_reply_markup *DS_RM) { if (!DS_RM) { return NULL; } struct tgl_message_reply_markup *R = talloc0 (sizeof (*R)); R->flags = DS_LVAL (DS_RM->flags); R->refcnt = 1; R->rows = DS_RM->rows ? DS_LVAL (DS_RM->rows->cnt) : 0; int total = 0; R->row_start = talloc ((R->rows + 1) * 4); R->row_start[0] = 0; int i; for (i = 0; i < R->rows; i++) { struct tl_ds_keyboard_button_row *DS_K = DS_RM->rows->data[i]; total += DS_LVAL (DS_K->buttons->cnt); R->row_start[i + 1] = total; } R->buttons = talloc (sizeof (void *) * total); int r = 0; for (i = 0; i < R->rows; i++) { struct tl_ds_keyboard_button_row *DS_K = DS_RM->rows->data[i]; int j; for (j = 0; j < DS_LVAL (DS_K->buttons->cnt); j++) { struct tl_ds_keyboard_button *DS_KB = DS_K->buttons->data[j]; R->buttons[r ++] = DS_STR_DUP (DS_KB->text); } } assert (r == total); return R; } /* }}} */ void tglp_insert_encrypted_chat (struct tgl_state *TLS, tgl_peer_t *P) { TLS->encr_chats_allocated ++; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, P, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = P; } void tglp_insert_user (struct tgl_state *TLS, tgl_peer_t *P) { TLS->users_allocated ++; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, P, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = P; } void tglp_insert_chat (struct tgl_state *TLS, tgl_peer_t *P) { TLS->chats_allocated ++; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, P, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = P; } void tglp_insert_channel (struct tgl_state *TLS, tgl_peer_t *P) { TLS->channels_allocated ++; TLS->peer_tree = tree_insert_peer (TLS->peer_tree, P, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = P; } void tgl_insert_empty_user (struct tgl_state *TLS, int uid) { tgl_peer_id_t id = TGL_MK_USER (uid); if (tgl_peer_get (TLS, id)) { return; } tgl_peer_t *P = talloc0 (sizeof (*P)); P->id = id; tglp_insert_user (TLS, P); } void tgl_insert_empty_chat (struct tgl_state *TLS, int cid) { tgl_peer_id_t id = TGL_MK_CHAT (cid); if (tgl_peer_get (TLS, id)) { return; } tgl_peer_t *P = talloc0 (sizeof (*P)); P->id = id; tglp_insert_chat (TLS, P); } /* {{{ Free */ void tgls_free_photo_size (struct tgl_state *TLS, struct tgl_photo_size *S) { tfree_str (S->type); if (S->data) { tfree (S->data, S->size); } } void tgls_free_photo (struct tgl_state *TLS, struct tgl_photo *P) { if (--P->refcnt) { assert (P->refcnt > 0); return; } if (P->caption) { tfree_str (P->caption); } if (P->sizes) { int i; for (i = 0; i < P->sizes_num; i++) { tgls_free_photo_size (TLS, &P->sizes[i]); } tfree (P->sizes, sizeof (struct tgl_photo_size) * P->sizes_num); } TLS->photo_tree = tree_delete_photo (TLS->photo_tree, P); tfree (P, sizeof (*P)); } void tgls_free_document (struct tgl_state *TLS, struct tgl_document *D) { if (--D->refcnt) { assert (D->refcnt); return; } if (D->mime_type) { tfree_str (D->mime_type);} if (D->caption) {tfree_str (D->caption);} tgls_free_photo_size (TLS, &D->thumb); TLS->document_tree = tree_delete_document (TLS->document_tree, D); tfree (D, sizeof (*D)); } void tgls_free_webpage (struct tgl_state *TLS, struct tgl_webpage *W) { if (--W->refcnt) { assert (W->refcnt); return; } if (W->url) { tfree_str (W->url); } if (W->display_url) { tfree_str (W->display_url); } if (W->title) { tfree_str (W->title); } if (W->site_name) { tfree_str (W->site_name); } if (W->type) { tfree_str (W->type); } if (W->description) { tfree_str (W->description); } if (W->photo) { tgls_free_photo (TLS, W->photo); } if (W->embed_url) { tfree_str (W->embed_url); } if (W->embed_type) { tfree_str (W->embed_type); } if (W->author) { tfree_str (W->author); } TLS->webpage_tree = tree_delete_webpage (TLS->webpage_tree, W); tfree (W, sizeof (*W)); } void tgls_free_message_media (struct tgl_state *TLS, struct tgl_message_media *M) { switch (M->type) { case tgl_message_media_none: case tgl_message_media_geo: return; case tgl_message_media_photo: if (M->photo) { tgls_free_photo (TLS, M->photo); } if (M->caption) { tfree_str (M->caption); } M->photo = NULL; return; case tgl_message_media_contact: tfree_str (M->phone); tfree_str (M->first_name); tfree_str (M->last_name); return; case tgl_message_media_document: case tgl_message_media_video: case tgl_message_media_audio: tgls_free_document (TLS, M->document); if (M->caption) { tfree_str (M->caption); } return; case tgl_message_media_unsupported: return; case tgl_message_media_document_encr: tfree_secure (M->encr_document->key, 32); tfree_secure (M->encr_document->iv, 32); tfree (M->encr_document, sizeof (*M->encr_document)); return; case tgl_message_media_webpage: tgls_free_webpage (TLS, M->webpage); return; case tgl_message_media_venue: if (M->venue.title) { tfree_str (M->venue.title); } if (M->venue.address) { tfree_str (M->venue.address); } if (M->venue.provider) { tfree_str (M->venue.provider); } if (M->venue.venue_id) { tfree_str (M->venue.venue_id); } return; default: vlogprintf (E_ERROR, "type = 0x%08x\n", M->type); assert (0); } } void tgls_free_message_action (struct tgl_state *TLS, struct tgl_message_action *M) { switch (M->type) { case tgl_message_action_none: return; case tgl_message_action_chat_create: tfree_str (M->title); tfree (M->users, M->user_num * 4); return; case tgl_message_action_chat_edit_title: tfree_str (M->new_title); return; case tgl_message_action_chat_edit_photo: tgls_free_photo (TLS, M->photo); M->photo = NULL; return; case tgl_message_action_chat_add_users: tfree (M->users, M->user_num * 4); return; case tgl_message_action_chat_delete_photo: case tgl_message_action_chat_add_user_by_link: case tgl_message_action_chat_delete_user: case tgl_message_action_geo_chat_create: case tgl_message_action_geo_chat_checkin: case tgl_message_action_set_message_ttl: case tgl_message_action_read_messages: case tgl_message_action_delete_messages: case tgl_message_action_screenshot_messages: case tgl_message_action_flush_history: case tgl_message_action_typing: case tgl_message_action_resend: case tgl_message_action_notify_layer: case tgl_message_action_commit_key: case tgl_message_action_abort_key: case tgl_message_action_noop: case tgl_message_action_migrated_to: return; case tgl_message_action_request_key: case tgl_message_action_accept_key: tfree (M->g_a, 256); return; case tgl_message_action_channel_create: case tgl_message_action_migrated_from: tfree_str (M->title); return; /* default: vlogprintf (E_ERROR, "type = 0x%08x\n", M->type); assert (0);*/ } vlogprintf (E_ERROR, "type = 0x%08x\n", M->type); assert (0); } void tgls_free_message_entity (struct tgl_state *TLS, struct tgl_message_entity *E) { if (E->extra) { tfree_str (E->extra); } } void tgls_clear_message (struct tgl_state *TLS, struct tgl_message *M) { if (!(M->flags & TGLMF_SERVICE)) { if (M->message) { tfree (M->message, M->message_len + 1); } tgls_free_message_media (TLS, &M->media); } else { tgls_free_message_action (TLS, &M->action); } int i; for (i = 0; i < M->entities_num; i++) { tgls_free_message_entity (TLS, &M->entities[i]); } tfree (M->entities, M->entities_num * sizeof (struct tgl_message_entity)); } void tgls_free_reply_markup (struct tgl_state *TLS, struct tgl_message_reply_markup *R) { if (!--R->refcnt) { int i; for (i = 0; i < R->row_start[R->rows]; i++) { tfree_str (R->buttons[i]); } tfree (R->buttons, R->row_start[R->rows] * sizeof (void *)); tfree (R->row_start, 4 * (R->rows + 1)); tfree (R, sizeof (*R)); } else { assert (R->refcnt > 0); } } void tgls_free_message (struct tgl_state *TLS, struct tgl_message *M) { tgls_clear_message (TLS, M); if (M->reply_markup) { tgls_free_reply_markup (TLS, M->reply_markup); } tfree (M, sizeof (*M)); } void tgls_free_chat (struct tgl_state *TLS, struct tgl_chat *U) { if (U->title) { tfree_str (U->title); } if (U->print_title) { tfree_str (U->print_title); } if (U->user_list) { tfree (U->user_list, U->user_list_size * 12); } if (U->photo) { tgls_free_photo (TLS, U->photo); } tfree (U, sizeof (tgl_peer_t)); } void tgls_free_user (struct tgl_state *TLS, struct tgl_user *U) { if (U->first_name) { tfree_str (U->first_name); } if (U->last_name) { tfree_str (U->last_name); } if (U->print_name) { tfree_str (U->print_name); } if (U->phone) { tfree_str (U->phone); } if (U->username) { tfree_str (U->username); } if (U->real_first_name) { tfree_str (U->real_first_name); } if (U->real_last_name) { tfree_str (U->real_last_name); } if (U->status.ev) { tgl_remove_status_expire (TLS, U); } if (U->photo) { tgls_free_photo (TLS, U->photo); } if (U->bot_info) { tgls_free_bot_info (TLS, U->bot_info); } tfree (U, sizeof (tgl_peer_t)); } void tgls_free_encr_chat (struct tgl_state *TLS, struct tgl_secret_chat *U) { if (U->print_name) { tfree_str (U->print_name); } if (U->g_key) { tfree (U->g_key, 256); } tfree (U, sizeof (tgl_peer_t)); } void tgls_free_channel (struct tgl_state *TLS, struct tgl_channel *U) { if (U->print_title) { tfree_str (U->print_title); } if (U->username) { tfree_str (U->username); } if (U->title) { tfree_str (U->title); } if (U->about) { tfree_str (U->about); } if (U->photo) { tgls_free_photo (TLS, U->photo); } tfree (U, sizeof (tgl_peer_t)); } void tgls_free_peer (struct tgl_state *TLS, tgl_peer_t *P) { if (tgl_get_peer_type (P->id) == TGL_PEER_USER) { tgls_free_user (TLS, (void *)P); } else if (tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { tgls_free_chat (TLS, (void *)P); } else if (tgl_get_peer_type (P->id) == TGL_PEER_ENCR_CHAT) { tgls_free_encr_chat (TLS, (void *)P); } else if (tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL) { tgls_free_channel (TLS, (void *)P); } else { assert (0); } } void tgls_free_bot_info (struct tgl_state *TLS, struct tgl_bot_info *B) { if (!B) { return; } int i; for (i = 0; i < B->commands_num; i++) { tfree_str (B->commands[i].command); tfree_str (B->commands[i].description); } tfree (B->commands, sizeof (struct tgl_bot_command) * B->commands_num); tfree_str (B->share_text); tfree_str (B->description); tfree (B, sizeof (*B)); } /* }}} */ /* Messages {{{ */ void tglm_message_del_use (struct tgl_state *TLS, struct tgl_message *M) { M->next_use->prev_use = M->prev_use; M->prev_use->next_use = M->next_use; } void tglm_message_add_use (struct tgl_state *TLS, struct tgl_message *M) { M->next_use = TLS->message_list.next_use; M->prev_use = &TLS->message_list; M->next_use->prev_use = M; M->prev_use->next_use = M; } void tglm_message_add_peer (struct tgl_state *TLS, struct tgl_message *M) { tgl_peer_id_t id; if (!tgl_cmp_peer_id (M->to_id, TLS->our_id)) { id = M->from_id; } else { id = M->to_id; } tgl_peer_t *P = tgl_peer_get (TLS, id); if (!P) { P = talloc0 (sizeof (*P)); P->id = id; switch (tgl_get_peer_type (id)) { case TGL_PEER_USER: TLS->users_allocated ++; break; case TGL_PEER_CHAT: TLS->chats_allocated ++; break; case TGL_PEER_GEO_CHAT: TLS->geo_chats_allocated ++; break; case TGL_PEER_ENCR_CHAT: TLS->encr_chats_allocated ++; break; } TLS->peer_tree = tree_insert_peer (TLS->peer_tree, P, rand ()); increase_peer_size (TLS); TLS->Peers[TLS->peer_num ++] = P; } if (!P->last) { P->last = M; M->prev = M->next = 0; } else { if (tgl_get_peer_type (P->id) != TGL_PEER_ENCR_CHAT) { struct tgl_message *N = P->last; struct tgl_message *NP = 0; while (N && N->permanent_id.id > M->permanent_id.id) { NP = N; N = N->next; } if (N) { assert (N->permanent_id.id < M->permanent_id.id); } M->next = N; M->prev = NP; if (N) { N->prev = M; } if (NP) { NP->next = M; } else { P->last = M; } } else { struct tgl_message *N = P->last; struct tgl_message *NP = 0; M->next = N; M->prev = NP; if (N) { N->prev = M; } if (NP) { NP->next = M; } else { P->last = M; } } } } void tglm_message_del_peer (struct tgl_state *TLS, struct tgl_message *M) { tgl_peer_id_t id; if (!tgl_cmp_peer_id (M->to_id, TLS->our_id)) { id = M->from_id; } else { id = M->to_id; } tgl_peer_t *P = tgl_peer_get (TLS, id); if (M->prev) { M->prev->next = M->next; } if (M->next) { M->next->prev = M->prev; } if (P && P->last == M) { P->last = M->next; } } struct tgl_message *tglm_message_alloc (struct tgl_state *TLS, tgl_message_id_t *id) { struct tgl_message *M = talloc0 (sizeof (*M)); M->permanent_id = *id; tglm_message_insert_tree (TLS, M); TLS->messages_allocated ++; return M; } void tglm_message_insert_tree (struct tgl_state *TLS, struct tgl_message *M) { assert (M->permanent_id.id); TLS->message_tree = tree_insert_message (TLS->message_tree, M, rand ()); } void tglm_message_remove_tree (struct tgl_state *TLS, struct tgl_message *M) { assert (M->permanent_id.id); TLS->message_tree = tree_delete_message (TLS->message_tree, M); } void tglm_message_insert (struct tgl_state *TLS, struct tgl_message *M) { tglm_message_add_use (TLS, M); tglm_message_add_peer (TLS, M); } void tglm_message_insert_unsent (struct tgl_state *TLS, struct tgl_message *M) { TLS->message_unsent_tree = tree_insert_message (TLS->message_unsent_tree, M, rand ()); } void tglm_message_remove_unsent (struct tgl_state *TLS, struct tgl_message *M) { TLS->message_unsent_tree = tree_delete_message (TLS->message_unsent_tree, M); } static void __send_msg (struct tgl_message *M, void *_TLS) { struct tgl_state *TLS = _TLS; vlogprintf (E_NOTICE, "Resending message...\n"); //print_message (M); if (M->media.type != tgl_message_media_none) { assert (M->flags & TGLMF_ENCRYPTED); bl_do_message_delete (TLS, &M->permanent_id); } else { tgl_do_send_msg (TLS, M, 0, 0); } } void tglm_send_all_unsent (struct tgl_state *TLS) { tree_act_ex_message (TLS->message_unsent_tree, __send_msg, TLS); } /* }}} */ struct tgl_photo *tgl_photo_get (struct tgl_state *TLS, long long id) { struct tgl_photo P; P.id = id; return tree_lookup_photo (TLS->photo_tree, &P); } void tgl_photo_insert (struct tgl_state *TLS, struct tgl_photo *P) { TLS->photo_tree = tree_insert_photo (TLS->photo_tree, P, rand ()); } struct tgl_document *tgl_document_get (struct tgl_state *TLS, long long id) { struct tgl_document P; P.id = id; return tree_lookup_document (TLS->document_tree, &P); } void tgl_document_insert (struct tgl_state *TLS, struct tgl_document *P) { TLS->document_tree = tree_insert_document (TLS->document_tree, P, rand ()); } struct tgl_webpage *tgl_webpage_get (struct tgl_state *TLS, long long id) { struct tgl_webpage P; P.id = id; return tree_lookup_webpage (TLS->webpage_tree, &P); } void tgl_webpage_insert (struct tgl_state *TLS, struct tgl_webpage *P) { TLS->webpage_tree = tree_insert_webpage (TLS->webpage_tree, P, rand ()); } void tglp_peer_insert_name (struct tgl_state *TLS, tgl_peer_t *P) { TLS->peer_by_name_tree = tree_insert_peer_by_name (TLS->peer_by_name_tree, P, rand ()); } void tglp_peer_delete_name (struct tgl_state *TLS, tgl_peer_t *P) { TLS->peer_by_name_tree = tree_delete_peer_by_name (TLS->peer_by_name_tree, P); } tgl_peer_t *tgl_peer_get (struct tgl_state *TLS, tgl_peer_id_t id) { static tgl_peer_t U; U.id = id; return tree_lookup_peer (TLS->peer_tree, &U); } struct tgl_message *tgl_message_get (struct tgl_state *TLS, tgl_message_id_t *msg_id) { struct tgl_message M; if (msg_id->peer_type == TGL_PEER_RANDOM_ID) { msg_id = tgls_get_local_by_random (TLS, msg_id->id); } else if (msg_id->peer_type == TGL_PEER_TEMP_ID) { msg_id = tgls_get_local_by_temp (TLS, msg_id->id); } if (!msg_id) { return NULL; } M.permanent_id = *msg_id; return tree_lookup_message (TLS->message_tree, &M); } tgl_peer_t *tgl_peer_get_by_name (struct tgl_state *TLS, const char *s) { static tgl_peer_t P; P.print_name = (void *)s; tgl_peer_t *R = tree_lookup_peer_by_name (TLS->peer_by_name_tree, &P); return R; } void tgl_peer_iterator_ex (struct tgl_state *TLS, void (*it)(tgl_peer_t *P, void *extra), void *extra) { tree_act_ex_peer (TLS->peer_tree, it, extra); } int tgl_complete_user_list (struct tgl_state *TLS, int index, const char *text, int len, char **R) { index ++; while (index < TLS->peer_num && (!TLS->Peers[index]->print_name || strncmp (TLS->Peers[index]->print_name, text, len) || tgl_get_peer_type (TLS->Peers[index]->id) != TGL_PEER_USER)) { index ++; } if (index < TLS->peer_num) { *R = strdup (TLS->Peers[index]->print_name); assert (*R); return index; } else { return -1; } } int tgl_complete_chat_list (struct tgl_state *TLS, int index, const char *text, int len, char **R) { index ++; while (index < TLS->peer_num && (!TLS->Peers[index]->print_name || strncmp (TLS->Peers[index]->print_name, text, len) || tgl_get_peer_type (TLS->Peers[index]->id) != TGL_PEER_CHAT)) { index ++; } if (index < TLS->peer_num) { *R = strdup (TLS->Peers[index]->print_name); assert (*R); return index; } else { return -1; } } int tgl_complete_channel_list (struct tgl_state *TLS, int index, const char *text, int len, char **R) { index ++; while (index < TLS->peer_num && (!TLS->Peers[index]->print_name || strncmp (TLS->Peers[index]->print_name, text, len) || tgl_get_peer_type (TLS->Peers[index]->id) != TGL_PEER_CHANNEL)) { index ++; } if (index < TLS->peer_num) { *R = strdup (TLS->Peers[index]->print_name); assert (*R); return index; } else { return -1; } } int tgl_complete_encr_chat_list (struct tgl_state *TLS, int index, const char *text, int len, char **R) { index ++; while (index < TLS->peer_num && (!TLS->Peers[index]->print_name || strncmp (TLS->Peers[index]->print_name, text, len) || tgl_get_peer_type (TLS->Peers[index]->id) != TGL_PEER_ENCR_CHAT)) { index ++; } if (index < TLS->peer_num) { *R = strdup (TLS->Peers[index]->print_name); assert (*R); return index; } else { return -1; } } int tgl_complete_peer_list (struct tgl_state *TLS, int index, const char *text, int len, char **R) { index ++; while (index < TLS->peer_num && (!TLS->Peers[index]->print_name || strncmp (TLS->Peers[index]->print_name, text, len))) { index ++; } if (index < TLS->peer_num) { *R = strdup (TLS->Peers[index]->print_name); assert (*R); return index; } else { return -1; } } int tgl_secret_chat_for_user (struct tgl_state *TLS, tgl_peer_id_t user_id) { int index = 0; while (index < TLS->peer_num && (tgl_get_peer_type (TLS->Peers[index]->id) != TGL_PEER_ENCR_CHAT || TLS->Peers[index]->encr_chat.user_id != tgl_get_peer_id (user_id) || TLS->Peers[index]->encr_chat.state != sc_ok)) { index ++; } if (index < TLS->peer_num) { return tgl_get_peer_id (TLS->Peers[index]->encr_chat.id); } else { return -1; } } void tgls_free_peer_gw (tgl_peer_t *P, void *TLS) { tgls_free_peer (TLS, P); } void tgls_free_message_gw (struct tgl_message *M, void *TLS) { tgls_free_message (TLS, M); } void tgls_remove_message_gw (struct tgl_state *TLS, struct tgl_message *M) { if (tree_lookup_message (TLS->message_unsent_tree, M)) { tglm_message_remove_unsent (TLS, M); } } void tgls_remove_and_free_message_gw (struct tgl_message *M, void *TLS) { tgls_remove_message_gw (TLS, M); tgls_free_message (TLS, M); } void tgl_free_all (struct tgl_state *TLS) { tree_act_ex_peer (TLS->peer_tree, tgls_free_peer_gw, TLS); TLS->peer_tree = tree_clear_peer (TLS->peer_tree); TLS->peer_by_name_tree = tree_clear_peer_by_name (TLS->peer_by_name_tree); tree_act_ex_message (TLS->message_tree, tgls_remove_and_free_message_gw, TLS); TLS->message_tree = tree_clear_message (TLS->message_tree); tree_act_ex_message (TLS->message_unsent_tree, tgls_free_message_gw, TLS); TLS->message_unsent_tree = tree_clear_message (TLS->message_unsent_tree); tglq_query_free_all (TLS); TLS->random_id_tree = tree_clear_random_id (TLS->random_id_tree); TLS->temp_id_tree = tree_clear_temp_id (TLS->temp_id_tree); if (TLS->encr_prime) { tfree (TLS->encr_prime, 256); } if (TLS->binlog_name) { tfree_str (TLS->binlog_name); } if (TLS->auth_file) { tfree_str (TLS->auth_file); } if (TLS->downloads_directory) { tfree_str (TLS->downloads_directory); } if (TLS->app_hash) { tfree_str (TLS->app_hash); } if (TLS->app_version) { tfree_str (TLS->app_version); } if (TLS->error) { tfree_str (TLS->error); } int i; for (i = 0; i < TLS->rsa_key_num; i++) { if (TLS->rsa_key_list[i]) { tfree_str (TLS->rsa_key_list[i]); } } for (i = 0; i <= TLS->max_dc_num; i++) if (TLS->DC_list[i]) { tgls_free_dc (TLS, TLS->DC_list[i]); } TGLC_bn_ctx_free (TLS->TGLC_bn_ctx); tgls_free_pubkey (TLS); if (TLS->ev_login) { TLS->timer_methods->free (TLS->ev_login); } if (TLS->online_updates_timer) { TLS->timer_methods->free (TLS->online_updates_timer); } tfree (TLS->Peers, TLS->peer_size * sizeof (void *)); tfree (TLS, sizeof (*TLS)); } int tgl_print_stat (struct tgl_state *TLS, char *s, int len) { return tsnprintf (s, len, "users_allocated\t%d\n" "chats_allocated\t%d\n" "encr_chats_allocated\t%d\n" "peer_num\t%d\n" "messages_allocated\t%d\n", TLS->users_allocated, TLS->chats_allocated, TLS->encr_chats_allocated, TLS->peer_num, TLS->messages_allocated ); } void tglf_fetch_int_array (int *dst, struct tl_ds_vector *src, int len) { int i; assert (len <= *src->f1); for (i = 0; i < len; i++) { dst[i] = *(int *)src->f2[i]; } } void tglf_fetch_int_tuple (int *dst, int **src, int len) { int i; for (i = 0; i < len; i++) { dst[i] = *src[i]; } } void tgls_messages_mark_read (struct tgl_state *TLS, struct tgl_message *M, int out, int seq) { while (M && M->permanent_id.id > seq) { if ((M->flags & TGLMF_OUT) == out) { if (!(M->flags & TGLMF_UNREAD)) { return; } } M = M->next; } while (M) { if ((M->flags & TGLMF_OUT) == out) { if (M->flags & TGLMF_UNREAD) { M->flags &= ~TGLMF_UNREAD; TLS->callback.marked_read (TLS, 1, &M); } else { return; } } M = M->next; } } /* void tgls_insert_random2local (struct tgl_state *TLS, long long random_id, tgl_message_id_t *msg_id) { struct random2local *X = talloc (sizeof (*X)); X->random_id = random_id; X->local_id = *msg_id; struct random2local *R = tree_lookup_random_id (TLS->random_id_tree, X); assert (!R); TLS->random_id_tree = tree_insert_random_id (TLS->random_id_tree, X, rand ()); } tgl_message_id_t *tgls_get_local_by_random (struct tgl_state *TLS, long long random_id) { struct random2local X; X.random_id = random_id; struct random2local *Y = tree_lookup_random_id (TLS->random_id_tree, &X); if (Y) { //TLS->random_id_tree = tree_delete_random_id (TLS->random_id_tree, Y); return &Y->local_id; } else { return NULL; } } void tgls_insert_temp2local (struct tgl_state *TLS, int temp_id, tgl_message_id_t *msg_id) { struct random2local *X = talloc (sizeof (*X)); X->random_id = temp_id; X->local_id = *msg_id; struct random2local *R = tree_lookup_random_id (TLS->temp_id_tree, X); assert (!R); TLS->temp_id_tree = tree_insert_random_id (TLS->temp_id_tree, X, rand ()); } }*/ tgl_message_id_t *tgls_get_local_by_random (struct tgl_state *TLS, long long random_id) { struct tgl_message M; M.random_id = random_id; struct tgl_message *N = tree_lookup_random_id (TLS->random_id_tree, &M); if (N) { return &N->permanent_id; } else { return NULL; } } tgl_message_id_t *tgls_get_local_by_temp (struct tgl_state *TLS, int temp_id) { struct tgl_message M; M.temp_id = temp_id; struct tgl_message *N = tree_lookup_temp_id (TLS->temp_id_tree, &M); if (N) { return &N->permanent_id; } else { return NULL; } } tgl_message_id_t tgl_convert_temp_msg_id (struct tgl_state *TLS, tgl_message_id_t msg_id) { struct tgl_message M; M.temp_id = msg_id.id; struct tgl_message *N = tree_lookup_temp_id (TLS->temp_id_tree, &M); if (N) { return N->permanent_id; } else { return msg_id; } } void tgls_message_change_temp_id (struct tgl_state *TLS, struct tgl_message *M, int temp_id) { if (M->temp_id == temp_id) { return; } assert (!M->temp_id); M->temp_id = temp_id; TLS->temp_id_tree = tree_insert_temp_id (TLS->temp_id_tree, M, rand ()); } void tgls_message_change_random_id (struct tgl_state *TLS, struct tgl_message *M, long long random_id) { if (M->random_id == random_id) { return; } assert (!M->random_id); M->random_id = random_id; TLS->random_id_tree = tree_insert_random_id (TLS->random_id_tree, M, rand ()); } void tglm_message_del_temp_id (struct tgl_state *TLS, struct tgl_message *M) { if (M->temp_id) { TLS->temp_id_tree = tree_delete_temp_id (TLS->temp_id_tree, M); } } void tglm_message_del_random_id (struct tgl_state *TLS, struct tgl_message *M) { if (M->random_id) { TLS->random_id_tree = tree_delete_random_id (TLS->random_id_tree, M); } }