summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tgl/structures.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tgl/structures.c')
-rw-r--r--protocols/Telegram/tgl/structures.c2311
1 files changed, 2311 insertions, 0 deletions
diff --git a/protocols/Telegram/tgl/structures.c b/protocols/Telegram/tgl/structures.c
new file mode 100644
index 0000000000..a680e4c415
--- /dev/null
+++ b/protocols/Telegram/tgl/structures.c
@@ -0,0 +1,2311 @@
+/*
+ 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 <assert.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include "tgl-structures.h"
+#include "mtproto-common.h"
+//#include "telegram.h"
+#include "tree.h"
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/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"
+
+#define sha1 SHA1
+#if defined(WIN32) || defined(_WIN32)
+#define bzero(b,len) (memset((b), '\0', (len)) )
+#endif
+
+struct random2local {
+ long long random_id;
+ int local_id;
+};
+
+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 random2local *L, struct random2local *R) {
+ if (L->random_id < R->random_id) { return -1; }
+ if (L->random_id > R->random_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 random2local *, random_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)
+
+
+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 = '@'; }
+ 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_new (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_new (DS_SMA);
+ free_ds_type_send_message_action (DS_SMA, TYPE_TO_PARAM (send_message_action));
+ return res;
+}
+
+/* {{{ Fetch */
+
+int tglf_fetch_file_location_new (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_new (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;
+}
+
+int tglf_fetch_user_new (struct tgl_state *TLS, struct tgl_user *U, struct tl_ds_user *DS_U) {
+ if (!DS_U) { return 0; }
+ U->id = TGL_MK_USER (DS_LVAL (DS_U->id));
+ if (DS_U->magic == CODE_user_empty) {
+ return 0;
+ }
+
+ int flags = U->flags & 0xffff;
+
+ if (DS_LVAL (DS_U->flags) & (1 << 10)) {
+ bl_do_set_our_id (TLS, tgl_get_peer_id (U->id));
+ flags |= TGLUF_SELF;
+ }
+
+ if (DS_LVAL (DS_U->flags) & (1 << 11)) {
+ flags |= TGLUF_CONTACT;
+ }
+
+ if (DS_LVAL (DS_U->flags) & (1 << 12)) {
+ flags |= TGLUF_MUTUAL_CONTACT;
+ }
+
+
+ if (DS_LVAL (DS_U->flags) & (1 << 14)) {
+ flags |= TGLUF_BOT;
+ }
+ /*
+ 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 (!(flags & TGLUF_CREATED)) {
+ flags |= TGLUF_CREATE | TGLUF_CREATED;
+ }
+
+ bl_do_user_new (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,
+ NULL, 0, NULL, 0,
+ DS_U->photo,
+ NULL, NULL,
+ NULL,
+ flags
+ );
+
+ if (DS_U->status) {
+ assert (tglf_fetch_user_status_new (TLS, &U->status, U, DS_U->status) >= 0);
+ }
+
+ if (DS_LVAL (DS_U->flags) & (1 << 13)) {
+ if (!(U->flags & TGLUF_DELETED)) {
+ bl_do_user_delete (TLS, U);
+ }
+ }
+
+ return 0;
+}
+
+void tglf_fetch_user_full_new (struct tgl_state *TLS, struct tgl_user *U, struct tl_ds_user_full *DS_UF) {
+ if (!DS_UF) { return; }
+
+ tglf_fetch_user_new (TLS, U, DS_UF->user);
+
+ int flags = U->flags & 0xffff;
+
+ if (DS_BVAL (DS_UF->blocked)) {
+ flags |= TGLUF_BLOCKED;
+ } else {
+ flags &= ~TGLUF_BLOCKED;
+ }
+
+ bl_do_user_new (TLS, tgl_get_peer_id (U->id),
+ NULL,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ DS_UF->profile_photo,
+ //DS_STR (DS_UF->real_first_name), DS_STR (DS_UF->real_last_name),
+ NULL, 0, NULL, 0,
+ NULL,
+ NULL, NULL,
+ DS_UF->bot_info,
+ flags
+ );
+}
+
+void str_to_256 (unsigned char *dst, char *src, int src_len) {
+ if (src_len >= 256) {
+ memcpy (dst, src + src_len - 256, 256);
+ } else {
+ bzero (dst, 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 {
+ bzero (dst, 32 - src_len);
+ memcpy (dst + 32 - src_len, src, src_len);
+ }
+}
+
+void tglf_fetch_encrypted_chat_new (struct tgl_state *TLS, struct tgl_secret_chat *U, struct tl_ds_encrypted_chat *DS_EC) {
+ if (!DS_EC) { return; }
+ U->id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_EC->id));
+ if (DS_EC->magic == CODE_encrypted_chat_empty) {
+ return;
+ }
+ 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;
+ }
+ bl_do_encr_chat_delete (TLS, U);
+ //write_secret_chat_file ();
+ return;
+ }
+
+ 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;
+ }
+
+ 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) - TLS->our_id;
+ int r = sc_request;
+ bl_do_encr_chat_new (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
+ );
+ } else {
+ if (DS_EC->magic == CODE_encrypted_chat_waiting) {
+ int r = sc_waiting;
+ bl_do_encr_chat_new (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
+ );
+ return; // 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_new (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
+ );
+ }
+}
+
+void tglf_fetch_chat_new (struct tgl_state *TLS, struct tgl_chat *C, struct tl_ds_chat *DS_C) {
+ if (!DS_C) { return; }
+
+ C->id = TGL_MK_CHAT (DS_LVAL (DS_C->id));
+ if (DS_C->magic == CODE_chat_empty) {
+ return;
+ }
+
+ int flags = C->flags & 0xffff;
+ if (!(flags & TGLCF_CREATED)) {
+ flags |= TGLCF_CREATE | TGLCF_CREATED;
+ }
+
+ bl_do_chat_new (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
+ );
+}
+
+void tglf_fetch_chat_full_new (struct tgl_state *TLS, struct tgl_chat *C, struct tl_ds_messages_chat_full *DS_MCF) {
+ if (!DS_MCF) { return; }
+ struct tl_ds_chat_full *DS_CF = DS_MCF->full_chat;
+
+ C->id = TGL_MK_CHAT (DS_LVAL (DS_CF->id));
+
+ bl_do_chat_new (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,
+ DS_CF->participants->admin_id,
+ NULL, NULL,
+ C->flags & 0xffff
+ );
+
+ if (DS_MCF->users) {
+ int i;
+ for (i = 0; i < DS_LVAL (DS_MCF->users->cnt); i++) {
+ tglf_fetch_alloc_user_new (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_new (TLS, DS_MCF->chats->data[i]);
+ }
+ }
+
+ 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_new (TLS, tgl_get_peer_id (P->id),
+ NULL,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL,
+ NULL, 0, NULL, 0,
+ NULL,
+ NULL, NULL,
+ DS_BI,
+ TGL_FLAGS_UNCHANGED
+ );
+ }
+ }
+ }
+}
+
+void tglf_fetch_photo_size_new (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_new (TLS, &S->loc, DS_PS->location);
+}
+
+void tglf_fetch_geo_new (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_new (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);
+ tglf_fetch_geo_new (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_new (TLS, &P->sizes[i], DS_P->sizes->data[i]);
+ }
+
+ return P;
+}
+
+struct tgl_document *tglf_fetch_alloc_video_new (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_new (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_new (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_new (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:
+ case CODE_document_attribute_sticker_l28:
+ 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_new (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_new (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_new (TLS, D, DS_D->attributes->data[i]);
+ }
+ }
+ return D;
+}
+
+struct tgl_webpage *tglf_fetch_alloc_webpage_new (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_new (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_new (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_new (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_user;
+ M->user = DS_LVAL (DS_MA->user_id);
+ 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;
+ default:
+ assert (0);
+ }
+}
+
+void tglf_fetch_message_short_new (struct tgl_state *TLS, struct tgl_message *M, 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;
+ }
+
+ 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;
+ int type = TGL_PEER_USER;
+
+ bl_do_create_message_new (TLS, DS_LVAL (DS_U->id),
+ (f & 2) ? &TLS->our_id : DS_U->user_id,
+ &type, (f & 2) ? DS_U->user_id : &TLS->our_id,
+ DS_U->fwd_from_id,
+ DS_U->fwd_date,
+ DS_U->date,
+ DS_STR (DS_U->message),
+ &A,
+ NULL,
+ DS_U->reply_to_msg_id,
+ NULL,
+ flags
+ );
+}
+
+void tglf_fetch_message_short_chat_new (struct tgl_state *TLS, struct tgl_message *M, struct tl_ds_updates *DS_U) {
+ tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_U->from_id)));
+ if (!P || !(P->flags & TGLPF_CREATED)) {
+ tgl_do_get_difference (TLS, 0, 0, 0);
+ return;
+ }
+ P = tgl_peer_get (TLS, TGL_MK_CHAT (DS_LVAL (DS_U->chat_id)));
+ if (!P || !(P->flags & TGLPF_CREATED)) {
+ tgl_do_get_difference (TLS, 0, 0, 0);
+ return;
+ }
+
+ 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;
+
+ int type = TGL_PEER_CHAT;
+ bl_do_create_message_new (TLS, DS_LVAL (DS_U->id),
+ DS_U->from_id,
+ &type, DS_U->chat_id,
+ DS_U->fwd_from_id,
+ DS_U->fwd_date,
+ DS_U->date,
+ DS_STR (DS_U->message),
+ &A,
+ NULL,
+ DS_U->reply_to_msg_id,
+ NULL,
+ flags
+ );
+}
+
+
+void tglf_fetch_message_media_new (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_new (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_new (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_new (TLS, DS_MM->audio);
+ break;
+ case CODE_message_media_document:
+ M->type = tgl_message_media_document;
+ M->document = tglf_fetch_alloc_document_new (TLS, DS_MM->document);
+ break;
+ case CODE_message_media_geo:
+ M->type = tgl_message_media_geo;
+ tglf_fetch_geo_new (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_unsupported:
+ //case CODE_message_media_unsupported_l22:
+ // M->type = tgl_message_media_unsupported;
+ // break;
+ case CODE_message_media_web_page:
+ M->type = tgl_message_media_webpage;
+ M->webpage = tglf_fetch_alloc_webpage_new (TLS, DS_MM->webpage);
+ break;
+ case CODE_message_media_venue:
+ M->type = tgl_message_media_venue;
+ tglf_fetch_geo_new (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;
+ default:
+ assert (0);
+ }
+}
+
+void tglf_fetch_message_media_encrypted_new (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_new (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++) {
+ struct tgl_message *N = tgl_message_get (TLS, DS_LVAL (DS_DMA->random_ids->data[i]));
+ 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_new (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);
+ }
+}
+
+tgl_peer_id_t tglf_fetch_peer_id_new (struct tgl_state *TLS, struct tl_ds_peer *DS_P) {
+ if (DS_P->magic == CODE_peer_user) {
+ return TGL_MK_USER (DS_LVAL (DS_P->user_id));
+ } else {
+ return TGL_MK_CHAT (DS_LVAL (DS_P->chat_id));
+ }
+}
+
+void tglf_fetch_message_new (struct tgl_state *TLS, struct tgl_message *M, struct tl_ds_message *DS_M) {
+ if (!DS_M || DS_M->magic == CODE_message_empty) { return; }
+
+ assert (M->id == DS_LVAL (DS_M->id));
+
+ tgl_peer_id_t to_id = tglf_fetch_peer_id_new (TLS, DS_M->to_id);
+ {
+ tgl_peer_t *P = tgl_peer_get (TLS, to_id);
+ if (!P || !(P->flags & TGLPF_CREATED)) {
+ tgl_do_get_difference (TLS, 0, 0, 0);
+ return;
+ }
+ P = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_M->from_id)));
+ if (!P || !(P->flags & TGLPF_CREATED)) {
+ tgl_do_get_difference (TLS, 0, 0, 0);
+ return;
+ }
+ }
+
+ int new = !(M->flags & TGLMF_CREATED);
+
+ if (new) {
+ int peer_id = tgl_get_peer_id (to_id);
+ int peer_type = tgl_get_peer_type (to_id);
+
+ 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;
+ }
+
+ bl_do_create_message_new (TLS, DS_LVAL (DS_M->id),
+ DS_M->from_id,
+ &peer_type, &peer_id,
+ DS_M->fwd_from_id, 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,
+ flags | TGLMF_CREATE | TGLMF_CREATED
+ );
+ }
+}
+
+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);
+ sha1 (buf, 48, sha1a_buffer);
+
+ memcpy (buf, e_key + 8, 16);
+ memcpy (buf + 16, msg_key, 16);
+ memcpy (buf + 32, e_key + 12, 16);
+ sha1 (buf, 48, sha1b_buffer);
+
+ memcpy (buf, e_key + 16, 32);
+ memcpy (buf + 32, msg_key, 16);
+ sha1 (buf, 48, sha1c_buffer);
+
+ memcpy (buf, msg_key, 16);
+ memcpy (buf + 16, e_key + 24, 32);
+ 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);
+
+ AES_KEY aes_key;
+ AES_set_decrypt_key (key, 256, &aes_key);
+ 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));
+ sha1 ((void *)decr_ptr, 4 + x, sha1a_buffer);
+
+ if (memcmp (sha1a_buffer + 4, msg_key, 16)) {
+ return -1;
+ }
+ return 0;
+}
+
+void tglf_fetch_encrypted_message_new (struct tgl_state *TLS, struct tgl_message *M, struct tl_ds_encrypted_message *DS_EM) {
+ if (!DS_EM) { return; }
+
+ int new = !(M->flags & TGLMF_CREATED);
+ if (!new) {
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ decr_ptr += 2;
+
+ if (decrypt_encrypted_message (&P->encr_chat) < 0) {
+ vlogprintf (E_WARNING, "can not decrypt message\n");
+ return;
+ }
+
+ 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;
+ }
+
+ 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_new (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
+ );
+
+ 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;
+ }
+
+ if ((in_seq_no & 1) != 1 - (P->encr_chat.admin_id == TLS->our_id) ||
+ (out_seq_no & 1) != (P->encr_chat.admin_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;
+ }
+ 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;
+ }
+ 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;
+ }
+
+ struct tl_ds_decrypted_message *DS_DM = DS_DML->message;
+ if (M->id != DS_LVAL (DS_DM->random_id)) {
+ vlogprintf (E_ERROR, "Incorrect message: id = %"_PRINTF_INT64_"d, new_id = %"_PRINTF_INT64_"d\n", M->id, DS_LVAL (DS_DM->random_id));
+ free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer));
+ return;
+ }
+
+ int peer_type = TGL_PEER_ENCR_CHAT;
+ int peer_id = tgl_get_peer_id (P->id);
+
+ bl_do_create_message_encr_new (TLS, M->id, &P->encr_chat.user_id, &peer_type, &peer_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_new (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
+ );
+ assert (P->encr_chat.in_seq_no == in_seq_no);
+ }
+
+ free_ds_type_decrypted_message_layer (DS_DML, TYPE_TO_PARAM(decrypted_message_layer));
+}
+
+void tglf_fetch_encrypted_message_file_new (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->id < M2->id) { return -1; }
+ else if (M1->id > M2->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_user *tglf_fetch_alloc_user_new (struct tgl_state *TLS, struct tl_ds_user *DS_U) {
+ tgl_peer_t *U = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_U->id)));
+ if (!U) {
+ TLS->users_allocated ++;
+ U = talloc0 (sizeof (*U));
+ U->id = TGL_MK_USER (DS_LVAL (DS_U->id));
+ TLS->peer_tree = tree_insert_peer (TLS->peer_tree, U, lrand48 ());
+ increase_peer_size (TLS);
+ TLS->Peers[TLS->peer_num ++] = U;
+ }
+ tglf_fetch_user_new (TLS, &U->user, DS_U);
+ return &U->user;
+}
+
+struct tgl_secret_chat *tglf_fetch_alloc_encrypted_chat_new (struct tgl_state *TLS, struct tl_ds_encrypted_chat *DS_EC) {
+ tgl_peer_t *U = tgl_peer_get (TLS, TGL_MK_ENCR_CHAT (DS_LVAL (DS_EC->id)));
+ if (!U) {
+ U = talloc0 (sizeof (*U));
+ U->id = TGL_MK_ENCR_CHAT (DS_LVAL (DS_EC->id));
+ TLS->encr_chats_allocated ++;
+ TLS->peer_tree = tree_insert_peer (TLS->peer_tree, U, lrand48 ());
+ increase_peer_size (TLS);
+ TLS->Peers[TLS->peer_num ++] = U;
+ }
+ tglf_fetch_encrypted_chat_new (TLS, &U->encr_chat, DS_EC);
+ return &U->encr_chat;
+}
+
+struct tgl_user *tglf_fetch_alloc_user_full_new (struct tgl_state *TLS, struct tl_ds_user_full *DS_U) {
+ tgl_peer_t *U = tgl_peer_get (TLS, TGL_MK_USER (DS_LVAL (DS_U->user->id)));
+ if (U) {
+ tglf_fetch_user_full_new (TLS, &U->user, DS_U);
+ return &U->user;
+ } else {
+ TLS->users_allocated ++;
+ U = talloc0 (sizeof (*U));
+ U->id = TGL_MK_USER (DS_LVAL (DS_U->user->id));
+ TLS->peer_tree = tree_insert_peer (TLS->peer_tree, U, lrand48 ());
+ tglf_fetch_user_full_new (TLS, &U->user, DS_U);
+ increase_peer_size (TLS);
+ TLS->Peers[TLS->peer_num ++] = U;
+ return &U->user;
+ }
+}
+
+struct tgl_message *tglf_fetch_alloc_message_new (struct tgl_state *TLS, struct tl_ds_message *DS_M) {
+ struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_M->id));
+
+ if (!M) {
+ M = tglm_message_alloc (TLS, DS_LVAL (DS_M->id));
+ }
+ tglf_fetch_message_new (TLS, M, DS_M);
+ return M;
+}
+
+struct tgl_message *tglf_fetch_alloc_encrypted_message_new (struct tgl_state *TLS, struct tl_ds_encrypted_message *DS_EM) {
+ struct tgl_message *M = tgl_message_get (TLS, DS_LVAL (DS_EM->random_id));
+
+ if (!M) {
+ M = talloc0 (sizeof (*M));
+ M->id = DS_LVAL (DS_EM->random_id);
+ tglm_message_insert_tree (TLS, M);
+ TLS->messages_allocated ++;
+ assert (tgl_message_get (TLS, M->id) == M);
+ }
+ tglf_fetch_encrypted_message_new (TLS, M, DS_EM);
+
+ 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_new (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
+ );
+ }
+ 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_new (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
+ );
+ }
+ }
+ return M;
+}
+
+struct tgl_message *tglf_fetch_alloc_message_short_new (struct tgl_state *TLS, struct tl_ds_updates *DS_U) {
+ int id = DS_LVAL (DS_U->id);
+ struct tgl_message *M = tgl_message_get (TLS, id);
+
+ if (!M) {
+ M = talloc0 (sizeof (*M));
+ M->id = id;
+ tglm_message_insert_tree (TLS, M);
+ TLS->messages_allocated ++;
+ }
+ tglf_fetch_message_short_new (TLS, M, DS_U);
+ return M;
+}
+
+struct tgl_message *tglf_fetch_alloc_message_short_chat_new (struct tgl_state *TLS, struct tl_ds_updates *DS_U) {
+ int id = DS_LVAL (DS_U->id);
+ struct tgl_message *M = tgl_message_get (TLS, id);
+
+ if (!M) {
+ M = talloc0 (sizeof (*M));
+ M->id = id;
+ tglm_message_insert_tree (TLS, M);
+ TLS->messages_allocated ++;
+ }
+ tglf_fetch_message_short_chat_new (TLS, M, DS_U);
+ return M;
+}
+
+struct tgl_chat *tglf_fetch_alloc_chat_new (struct tgl_state *TLS, struct tl_ds_chat *DS_C) {
+ tgl_peer_t *U = tgl_peer_get (TLS, TGL_MK_CHAT (DS_LVAL (DS_C->id)));
+ if (!U) {
+ TLS->chats_allocated ++;
+ U = talloc0 (sizeof (*U));
+ U->id = TGL_MK_CHAT (DS_LVAL (DS_C->id));
+ TLS->peer_tree = tree_insert_peer (TLS->peer_tree, U, lrand48 ());
+ increase_peer_size (TLS);
+ TLS->Peers[TLS->peer_num ++] = U;
+ }
+ tglf_fetch_chat_new (TLS, &U->chat, DS_C);
+ return &U->chat;
+}
+
+struct tgl_chat *tglf_fetch_alloc_chat_full_new (struct tgl_state *TLS, struct tl_ds_messages_chat_full *DS_MCF) {
+ tgl_peer_t *U = tgl_peer_get (TLS, TGL_MK_CHAT (DS_LVAL (DS_MCF->full_chat->id)));
+ if (U) {
+ tglf_fetch_chat_full_new (TLS, &U->chat, DS_MCF);
+ return &U->chat;
+ } else {
+ TLS->chats_allocated ++;
+ U = talloc0 (sizeof (*U));
+ U->id = TGL_MK_CHAT (DS_LVAL (DS_MCF->full_chat->id));
+ TLS->peer_tree = tree_insert_peer (TLS->peer_tree, U, lrand48 ());
+ tglf_fetch_chat_full_new (TLS, &U->chat, DS_MCF);
+ increase_peer_size (TLS);
+ TLS->Peers[TLS->peer_num ++] = U;
+ return &U->chat;
+ }
+}
+
+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, lrand48 ());
+ 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, lrand48 ());
+ 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, lrand48 ());
+ 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:
+ tgls_free_photo (TLS, M->photo);
+ 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);
+ return;
+ case tgl_message_media_unsupported:
+ tfree (M->data, M->data_size);
+ 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_delete_photo:
+ case tgl_message_action_chat_add_user:
+ 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:
+ return;
+ case tgl_message_action_request_key:
+ case tgl_message_action_accept_key:
+ tfree (M->g_a, 256);
+ 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_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);
+ }
+}
+
+void tgls_free_reply_markup (struct tgl_state *TLS, struct tgl_message_reply_markup *R) {
+ if (!--R->refcnt) {
+ 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 (*U));
+}
+
+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->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); }
+ tfree (U, sizeof (*U));
+}
+
+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 (*U));
+}
+
+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 {
+ 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, TGL_MK_USER (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, lrand48 ());
+ 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->id > M->id) {
+ NP = N;
+ N = N->next;
+ }
+ if (N) {
+ assert (N->id < M->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, TGL_MK_USER (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, long long id) {
+ struct tgl_message *M = talloc0 (sizeof (*M));
+ M->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->id);
+ TLS->message_tree = tree_insert_message (TLS->message_tree, M, lrand48 ());
+}
+
+void tglm_message_remove_tree (struct tgl_state *TLS, struct tgl_message *M) {
+ assert (M->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, lrand48 ());
+}
+
+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);
+ } 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, lrand48 ());
+}
+
+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, lrand48 ());
+}
+
+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, lrand48 ());
+}
+
+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, lrand48 ());
+}
+
+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, long long id) {
+ struct tgl_message M;
+ M.id = 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_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 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_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);
+
+ 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); }
+
+ int i;
+ for (i = 0; i < TLS->rsa_key_num; 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]);
+ }
+ BN_CTX_free (TLS->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); }
+}
+
+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->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, int local_id) {
+ struct random2local *X = talloc (sizeof (*X));
+ X->random_id = random_id;
+ X->local_id = local_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, lrand48 ());
+}
+
+int 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);
+ int y = Y->local_id;
+ tfree (Y, sizeof (*Y));
+ return y;
+ } else {
+ return 0;
+ }
+}