diff --git a/Makefile.in b/Makefile.in index e866d61..c78c62f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -23,10 +23,11 @@ LIB_LIST=${LIB}/libtgl.a TG_OBJECTS=${OBJ}/main.o ${OBJ}/loop.o ${OBJ}/interface.o ${OBJ}/lua-tg.o TGL_OBJECTS=${OBJ}/net.o ${OBJ}/mtproto-common.o ${OBJ}/mtproto-client.o ${OBJ}/queries.o ${OBJ}/structures.o ${OBJ}/binlog.o ${OBJ}/auto/auto.o ${OBJ}/tgl.o ${OBJ}/updates.o TLC_OBJECTS=${OBJ}/tlc.o ${OBJ}/tl-parser.o ${OBJ}/crc32.o +TLD_OBJECTS=${OBJ}/dump-tl-file.o GENERATE_OBJECTS=${OBJ}/generate.o COMMON_OBJECTS=${OBJ}/tools.o -OBJ_LIST=${TG_OBJECTS} ${TLC_OBJECTS} ${GENERATE_OBJECTS} ${COMMON_OBJECTS} ${TGL_OBJECTS} -OBJ_C=${TLC_OBJECTS} ${GENERATE_OBJECTS} ${COMMON_OBJECTS} ${TGL_OBJECTS} +OBJ_LIST=${TG_OBJECTS} ${TLC_OBJECTS} ${GENERATE_OBJECTS} ${COMMON_OBJECTS} ${TGL_OBJECTS} ${TLD_OBJECTS} +OBJ_C=${TLC_OBJECTS} ${GENERATE_OBJECTS} ${COMMON_OBJECTS} ${TGL_OBJECTS} ${TLD_OBJECTS} DEPENDENCE=$(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJ_LIST})) DEPENDENCE_LIST=${DEPENDENCE} @@ -41,6 +42,7 @@ CC=@CC@ all: ${EXE_LIST} ${DIR_LIST} ${LIB_LIST} create_dirs_and_headers: ${DIR_LIST} ${AUTO}/auto.c ${AUTO}/auto-header.h ${AUTO}/constants.h create_dirs: ${DIR_LIST} +dump-tl: ${EXE}/dump-tl-file ${DIR_LIST}: @test -d $@ || mkdir -p $@ @@ -85,6 +87,9 @@ ${AUTO}/auto-header.h: ${AUTO}/scheme.tlo ${EXE}/generate ${AUTO}/constants.h: ${AUTO}/scheme2.tl ${srcdir}/gen_constants_h.awk awk -f ${srcdir}/gen_constants_h.awk < $< > $@ +${EXE}/dump-tl-file: ${OBJ}/auto/auto.o ${TLD_OBJECTS} + ${CC} ${OBJ}/auto/auto.o ${TLD_OBJECTS} ${LINK_FLAGS} -o $@ + clean: rm -rf ${DIR_LIST} config.log config.status > /dev/null || echo "all clean" diff --git a/README-LUA b/README-LUA index 7c2853a..6553d79 100644 --- a/README-LUA +++ b/README-LUA @@ -74,5 +74,7 @@ Function_list (arguments are listed aside from cb_function and cb_extra, : status_online () status_offline () + send_location (peer, latitude, longitude) + Also, you have function postpone (cb_function, cb_extra, timeout). It will call your cb_function in specified number of seconds (number of seconds may be double). diff --git a/auto-static-print.c b/auto-static-print.c new file mode 100644 index 0000000..0c42764 --- /dev/null +++ b/auto-static-print.c @@ -0,0 +1,461 @@ +/* + 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 2014 +*/ + +#include +#include +#include + +static int cur_token_len; +static char *cur_token; +static int cur_token_real_len; +static int cur_token_quoted; +static int multiline_output = 1; +static int multiline_offset; +static int multiline_offset_size = 2; + +static int *in_ptr, *in_end; + +static inline int fetch_int (void) { + return *(in_ptr ++); +} + +static inline int fetch_long (void) { + long long r = *(long long *)in_ptr; + in_ptr += 2; + return r; +} + +static inline int fetch_long (void) { + long long r = *(long long *)in_ptr; + in_ptr += 2; + return r; +} + +static inline void out_int (int a) {} +static inline void out_double (double a) {} +static inline void out_string (char *s, int l) {} +static inline void out_long (long long a) {} + +static int disable_field_names; + +#define expect_token(token,len) \ + if (len != cur_token_len || memcmp (cur_token, token, cur_token_len)) { return -1; } \ + local_next_token (); + +#define expect_token_ptr(token,len) \ + if (len != cur_token_len || memcmp (cur_token, token, cur_token_len)) { return 0; } \ + local_next_token (); + +#define expect_token_autocomplete(token,len) \ + if (cur_token_len == -3 && len >= cur_token_real_len && !memcmp (cur_token, token, cur_token_real_len)) { set_autocomplete_string (token); return -1; }\ + if (len != cur_token_len || memcmp (cur_token, token, cur_token_len)) { return -1; } \ + local_next_token (); + +#define expect_token_ptr_autocomplete(token,len) \ + if (cur_token_len == -3 && len >= cur_token_real_len && !memcmp (cur_token, token, cur_token_real_len)) { set_autocomplete_string (token); return 0; }\ + if (len != cur_token_len || memcmp (cur_token, token, cur_token_len)) { return 0; } \ + local_next_token (); + + +static int autocomplete_mode; +static char *autocomplete_string; +static int (*autocomplete_fun)(const char *, int, int, char **); + +static void set_autocomplete_string (const char *s) { + if (autocomplete_string) { free (autocomplete_string); } + autocomplete_string = strdup (s); + assert (autocomplete_string); + autocomplete_mode = 1; +} + +static void set_autocomplete_type (int (*f)(const char *, int, int, char **)) { + autocomplete_fun = f; + autocomplete_mode = 2; +} + +static int is_int (void) { + if (cur_token_len <= 0) { return 0; } + char c = cur_token[cur_token_len]; + cur_token[cur_token_len] = 0; + char *p = 0; + + if (strtoll (cur_token, &p, 10)) {} + cur_token[cur_token_len] = c; + + return p == cur_token + cur_token_len; +} + +static long long get_int (void) { + if (cur_token_len <= 0) { return 0; } + char c = cur_token[cur_token_len]; + cur_token[cur_token_len] = 0; + char *p = 0; + + long long val = strtoll (cur_token, &p, 0); + cur_token[cur_token_len] = c; + + return val; +} + +static int is_double (void) { + if (cur_token_len <= 0) { return 0; } + char c = cur_token[cur_token_len]; + cur_token[cur_token_len] = 0; + char *p = 0; + + if (strtod (cur_token, &p)) {} + cur_token[cur_token_len] = c; + + return p == cur_token + cur_token_len; +} + +static double get_double (void) { + if (cur_token_len <= 0) { return 0; } + char c = cur_token[cur_token_len]; + cur_token[cur_token_len] = 0; + char *p = 0; + + double val = strtod (cur_token, &p); + cur_token[cur_token_len] = c; + + return val; +} + +static struct paramed_type *paramed_type_dup (struct paramed_type *P) { + if (ODDP (P)) { return P; } + struct paramed_type *R = malloc (sizeof (*R)); + assert (R); + R->type = malloc (sizeof (*R->type)); + assert (R->type); + memcpy (R->type, P->type, sizeof (*P->type)); + R->type->id = strdup (P->type->id); + assert (R->type->id); + + if (P->type->params_num) { + R->params = malloc (sizeof (void *) * P->type->params_num); + assert (R->params); + int i; + for (i = 0; i < P->type->params_num; i++) { + R->params[i] = paramed_type_dup (P->params[i]); + } + } + return R; +} + +void tgl_paramed_type_free (struct paramed_type *P) { + if (ODDP (P)) { return; } + if (P->type->params_num) { + int i; + for (i = 0; i < P->type->params_num; i++) { + tgl_paramed_type_free (P->params[i]); + } + free (P->params); + } + free (P->type->id); + free (P->type); + free (P); +} + +static char *buffer_pos, *buffer_end; + +static int is_wspc (char c) { + return c <= 32 && c > 0; +} + +static void skip_wspc (void) { + while (buffer_pos < buffer_end && is_wspc (*buffer_pos)) { + buffer_pos ++; + } +} + +static int is_letter (char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-'; +} + + +static char exp_buffer[1 << 25];; +static int exp_buffer_pos; + +static inline int is_hex (char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); +} + +static inline int hex2dec (char c) { + if (c >= '0' && c <= '9') { return c - '0'; } + else { return c - 'a' + 10; } +} + +static void expand_backslashed (char *s, int len) { + int backslashed = 0; + exp_buffer_pos = 0; + int i = 0; + while (i < len) { + assert (i + 3 <= (1 << 25)); + if (backslashed) { + backslashed = 0; + switch (s[i ++]) { + case 'n': + exp_buffer[exp_buffer_pos ++] = '\n'; + break; + case 'r': + exp_buffer[exp_buffer_pos ++] = '\r'; + break; + case 't': + exp_buffer[exp_buffer_pos ++] = '\t'; + break; + case 'b': + exp_buffer[exp_buffer_pos ++] = '\b'; + break; + case 'a': + exp_buffer[exp_buffer_pos ++] = '\a'; + break; + case '\\': + exp_buffer[exp_buffer_pos ++] = '\\'; + break; + case 'x': + if (i + 2 > len || !is_hex (s[i]) || !is_hex (s[i + 1])) { + exp_buffer_pos = -1; + return; + } + exp_buffer[exp_buffer_pos ++] = hex2dec (s[i]) * 16 + hex2dec (s[i + 1]); + i += 2; + break; + default: + break; + } + } else { + if (s[i] == '\\') { + backslashed = 1; + i ++; + } else { + exp_buffer[exp_buffer_pos ++] = s[i ++]; + } + } + } +} + +static void local_next_token (void) { + skip_wspc (); + cur_token_quoted = 0; + if (buffer_pos >= buffer_end) { + cur_token_len = -3; + cur_token_real_len = 0; + return; + } + char c = *buffer_pos; + if (is_letter (c)) { + cur_token = buffer_pos; + while (buffer_pos < buffer_end && is_letter (*buffer_pos)) { + buffer_pos ++; + } + if (buffer_pos < buffer_end) { + cur_token_len = buffer_pos - cur_token; + } else { + cur_token_real_len = buffer_pos - cur_token; + cur_token_len = -3; + } + return; + } else if (c == '"') { + cur_token_quoted = 1; + cur_token = buffer_pos ++; + int backslashed = 0; + while (buffer_pos < buffer_end && (*buffer_pos != '"' || backslashed)) { + if (*buffer_pos == '\\') { + backslashed ^= 1; + } else { + backslashed = 0; + } + buffer_pos ++; + } + if (*buffer_pos == '"') { + buffer_pos ++; + expand_backslashed (cur_token + 1, buffer_pos - cur_token - 2); + if (exp_buffer_pos < 0) { + cur_token_len = -2; + } else { + cur_token_len = exp_buffer_pos; + cur_token = exp_buffer; + } + } else { + cur_token_len = -2; + } + return; + } else { + if (c) { + cur_token = buffer_pos ++; + cur_token_len = 1; + } else { + cur_token_len = -3; + cur_token_real_len = 0; + } + } +} + +#define MAX_FVARS 100 +static struct paramed_type *fvars[MAX_FVARS]; +static int fvars_pos; + +static void add_var_to_be_freed (struct paramed_type *P) { + assert (fvars_pos < MAX_FVARS); + fvars[fvars_pos ++] = P; +} + +static void free_vars_to_be_freed (void) { + int i; + for (i = 0; i < fvars_pos; i++) { + tgl_paramed_type_free (fvars[i]); + } + fvars_pos = 0; +} + +int tglf_extf_autocomplete (const char *text, int text_len, int index, char **R, char *data, int data_len) { + if (index == -1) { + buffer_pos = data; + buffer_end = data + data_len; + autocomplete_mode = 0; + local_next_token (); + struct paramed_type *P = autocomplete_function_any (); + free_vars_to_be_freed (); + if (P) { tgl_paramed_type_free (P); } + } + if (autocomplete_mode == 0) { return -1; } + int len = strlen (text); + if (autocomplete_mode == 1) { + if (index >= 0) { return -1; } + index = 0; + if (!strncmp (text, autocomplete_string, len)) { + *R = strdup (autocomplete_string); + assert (*R); + return index; + } else { + return -1; + } + } else { + return autocomplete_fun (text, len, index, R); + } +} + +struct paramed_type *tglf_extf_store (const char *data, int data_len) { + buffer_pos = (char *)data; + buffer_end = (char *)(data + data_len); + local_next_token (); + return store_function_any (); +} + +#define OUT_BUF_SIZE (1 << 25) +static char out_buf[OUT_BUF_SIZE]; +static int out_buf_pos; + +#define eprintf(...) \ + do { \ + out_buf_pos += snprintf (out_buf + out_buf_pos, OUT_BUF_SIZE - out_buf_pos, __VA_ARGS__);\ + assert (out_buf_pos < OUT_BUF_SIZE);\ + } while (0)\ + +static int valid_utf8_char (const char *str) { + unsigned char c = (unsigned char) *str; + int n = 0; + + if ((c & 0x80) == 0x00) { + n = 0; + } else if ((c & 0xe0) == 0xc0) { + n = 1; + } else if ((c & 0xf0) == 0xe0) { + n = 2; + } else if ((c & 0xf8) == 0xf0) { + n = 3; + } else if ((c & 0xfc) == 0xf8) { + n = 4; + } else if ((c & 0xfe) == 0xfc) { + n = 5; + } else { + return -1; + } + + int i; + for (i = 0; i < n; i ++) { + if ((((unsigned char)(str[i + 1])) & 0xc0) != 0x80) { + return -1; + } + } + return n + 1; +} + +static void print_escaped_string (const char *str, int len) { + eprintf ("\""); + const char *end = str + len; + while (str < end) { + int n = valid_utf8_char (str); + if (n < 0) { + eprintf ("\\x%02x", (int)(unsigned char)*str); + str ++; + } else if (n >= 2) { + int i; + for (i = 0; i < n; i++) { + eprintf ("%c", *(str ++)); + } + } else if (((unsigned char)*str) >= ' ' && *str != '"' && *str != '\\') { + eprintf ("%c", *str); + str ++; + } else { + switch (*str) { + case '\n': + eprintf("\\n"); + break; + case '\r': + eprintf("\\r"); + break; + case '\t': + eprintf("\\t"); + break; + case '\b': + eprintf("\\b"); + break; + case '\a': + eprintf("\\a"); + break; + case '\\': + eprintf ("\\\\"); + break; + case '"': + eprintf ("\\\""); + break; + default: + eprintf ("\\x%02x", (int)(unsigned char)*str); + break; + } + str ++; + } + } + eprintf ("\""); +} + +static void print_offset (void) { + int i; + for (i = 0; i < multiline_offset; i++) { + eprintf (" "); + } +} + +char *tglf_extf_fetch (struct paramed_type *T) { + out_buf_pos = 0; + fetch_type_any (T); + return out_buf; +} diff --git a/auto-static.c b/auto-static.c index 1c058ac..05c3ed4 100644 --- a/auto-static.c +++ b/auto-static.c @@ -432,6 +432,6 @@ static void print_offset (void) { char *tglf_extf_fetch (struct paramed_type *T) { out_buf_pos = 0; - fetch_type_any (T); + if (fetch_type_any (T) < 0) { return 0; } return out_buf; } diff --git a/binlog.c b/binlog.c index 4cfb948..3557c8e 100644 --- a/binlog.c +++ b/binlog.c @@ -782,6 +782,12 @@ static int fetch_comb_binlog_send_message_text (void *extra) { } M->to_id = tgl_set_peer_id (t, fetch_int ()); + if (t == TGL_PEER_ENCR_CHAT) { + tgl_peer_t *P = tgl_peer_get (M->to_id); + if (P) { + P->encr_chat.out_seq_no ++; + } + } M->date = fetch_int (); int l = prefetch_strlen (); @@ -824,6 +830,11 @@ static int fetch_comb_binlog_send_message_action_encr (void *extra) { M->media.type = tgl_message_media_none; tglf_fetch_message_action_encrypted (&M->action); + + tgl_peer_t *P = tgl_peer_get (M->to_id); + if (P) { + P->encr_chat.out_seq_no ++; + } M->unread = 1; M->out = tgl_get_peer_id (M->from_id) == tgl_state.our_id; @@ -952,6 +963,11 @@ static int fetch_comb_binlog_create_message_media_encr_pending (void *extra) { int t = fetch_int (); M->to_id = tgl_set_peer_id (t, fetch_int ()); M->date = fetch_int (); + + tgl_peer_t *P = tgl_peer_get (M->to_id); + if (P) { + P->encr_chat.out_seq_no ++; + } int l = prefetch_strlen (); M->message = talloc (l + 1); @@ -1316,13 +1332,53 @@ static void create_new_binlog (void) { static int s[1000]; packet_ptr = s; out_int (CODE_binlog_start); - out_int (CODE_binlog_dc_option); - out_int (tgl_state.test_mode ? TG_SERVER_TEST_DC : TG_SERVER_DC); - out_string (""); - out_string (tgl_state.test_mode ? TG_SERVER_TEST : TG_SERVER); - out_int (443); - out_int (CODE_binlog_default_dc); - out_int (tgl_state.test_mode ? TG_SERVER_TEST_DC : TG_SERVER_DC); + if (tgl_state.test_mode) { + out_int (CODE_binlog_dc_option); + out_int (1); + out_string (""); + out_string (TG_SERVER_TEST_1); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (2); + out_string (""); + out_string (TG_SERVER_TEST_2); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (3); + out_string (""); + out_string (TG_SERVER_TEST_3); + out_int (443); + out_int (CODE_binlog_default_dc); + out_int (2); + } else { + out_int (CODE_binlog_dc_option); + out_int (1); + out_string (""); + out_string (TG_SERVER_1); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (2); + out_string (""); + out_string (TG_SERVER_2); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (3); + out_string (""); + out_string (TG_SERVER_3); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (4); + out_string (""); + out_string (TG_SERVER_4); + out_int (443); + out_int (CODE_binlog_dc_option); + out_int (5); + out_string (""); + out_string (TG_SERVER_5); + out_int (443); + out_int (CODE_binlog_default_dc); + out_int (2); + } int fd = open (get_binlog_file_name (), O_WRONLY | O_EXCL | O_CREAT, 0600); if (fd < 0) { @@ -1542,7 +1598,7 @@ void bl_do_user_set_friend (struct tgl_user *U, int friend) { void bl_do_dc_option (int id, int l1, const char *name, int l2, const char *ip, int port) { struct tgl_dc *DC = tgl_state.DC_list[id]; - if (DC) { return; } + if (DC && !strncmp (ip, DC->ip, l2)) { return; } clear_packet (); out_int (CODE_binlog_dc_option); @@ -1717,7 +1773,7 @@ void bl_do_encr_chat_init (int id, int user_id, unsigned char random[], unsigned void bl_do_set_pts (int pts) { if (tgl_state.locks & TGL_LOCK_DIFF) { return; } - if (pts == tgl_state.pts) { return; } + if (pts <= tgl_state.pts) { return; } int *ev = alloc_log_event (8); ev[0] = CODE_binlog_set_pts; ev[1] = pts; @@ -1726,7 +1782,7 @@ void bl_do_set_pts (int pts) { void bl_do_set_qts (int qts) { if (tgl_state.locks & TGL_LOCK_DIFF) { return; } - if (qts == tgl_state.qts) { return; } + if (qts <= tgl_state.qts) { return; } int *ev = alloc_log_event (8); ev[0] = CODE_binlog_set_qts; ev[1] = qts; @@ -1735,7 +1791,7 @@ void bl_do_set_qts (int qts) { void bl_do_set_date (int date) { if (tgl_state.locks & TGL_LOCK_DIFF) { return; } - if (date == tgl_state.date) { return; } + if (date <= tgl_state.date) { return; } int *ev = alloc_log_event (8); ev[0] = CODE_binlog_set_date; ev[1] = date; diff --git a/debian/changelog b/debian/changelog index c83e5f7..5b7ff4b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -telegram-cli (0.1-1) unstable; urgency=low +telegram-cli (1.0.5.1-1) unstable; urgency=low - * Initial release (Closes #737563) + * Initial release - -- Cleto Martín Mon, 03 Feb 2014 20:00:03 +0000 + -- Steve Illichevsky Wed, 01 Oct 2014 21:06:28 +0400 diff --git a/debian/compat b/debian/compat index ec63514..45a4fb7 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -9 +8 diff --git a/debian/control b/debian/control index 5e7e1f0..06be8de 100644 --- a/debian/control +++ b/debian/control @@ -1,16 +1,16 @@ Source: telegram-cli Section: net -Priority: optional -Maintainer: Cleto Martín -Build-Depends: debhelper (>= 8.0.0), - autotools-dev, - autoconf-archive, - libreadline-dev, - libconfig-dev, - libssl-dev, - lua5.2, - liblua5.2-dev -Standards-Version: 3.9.4 +Priority: extra +Maintainer: Steve Illichevsky +Build-Depends: debhelper (>= 8.0.0), + autotools-dev, + autoconf-archive, + libreadline-dev, + libconfig-dev, + libssl-dev, + lua5.1, + liblua5.1-dev +Standards-Version: 3.9.2 Homepage: https://github.com/vysheng/tg Vcs-Git: git://github.com/vysheng/tg.git Vcs-Browser: https://github.com/vysheng/tg @@ -33,4 +33,4 @@ Description: Command-line interface for Telegram messenger * Group chat: create and manage groups. * Secret chat: secured and encrypted conversations. * Contact management: add/edit/remove contacts. - * Multimedia support: send/load photos and videos. + * Multimedia support: send/load photos and videos. \ No newline at end of file diff --git a/debian/copyright b/debian/copyright index 2e46990..add9298 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,19 +1,19 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: http://dep.debian.net/deps/dep5 Upstream-Name: telegram-cli Source: https://github.com/vysheng/tg Files: * -Copyright: 2013 Vitaly Valtman -License: GPL-2.0+ +Copyright: 2013-2014 Vitaly Valtman +License: GPL-3.0+ Files: debian/* -Copyright: 2014 Cleto Martín -License: GPL-2.0+ +Copyright: 2014 Steve Illichevsky +License: GPL-3.0+ -License: GPL-2.0+ - This package is free software; you can redistribute it and/or modify +License: GPL-3.0+ + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This package is distributed in the hope that it will be useful, @@ -22,7 +22,10 @@ License: GPL-2.0+ GNU General Public License for more details. . You should have received a copy of the GNU General Public License - along with this program. If not, see + along with this program. If not, see . . On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. diff --git a/debian/docs b/debian/docs index b43bf86..227dafc 100644 --- a/debian/docs +++ b/debian/docs @@ -1 +1,4 @@ +README.es +README-LUA README.md +#telegram-cli.8 diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..cc7e0ea --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +telegram-cli_1.0.5.1-1_i386.deb net extra diff --git a/debian/install b/debian/install index f0f94de..27a3105 100644 --- a/debian/install +++ b/debian/install @@ -1 +1,3 @@ -telegram usr/bin +bin/telegram-cli usr/bin +bin/tlc usr/bin +server.pub etc/telegram-cli diff --git a/debian/rules b/debian/rules index 0f41aca..6d3c781 100755 --- a/debian/rules +++ b/debian/rules @@ -1,17 +1,23 @@ #!/usr/bin/make -f # -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 VERSION=$(shell dpkg-parsechangelog | sed -n 's/^Version: //p' | cut -f1 -d'-') PACKAGE_NAME=$(shell dpkg-parsechangelog | sed -n 's/^Source: //p') - %: - dh $@ --with autotools-dev - + dh $@ --with autotools-dev build-orig: - mkdir -p $(PACKAGE_NAME)-$(VERSION) - tar --exclude=ax_lua.m4 --exclude=debian --exclude=\.pc \ - --exclude=$(PACKAGE_NAME)-$(VERSION) -cf - . \ - | ( cd $(PACKAGE_NAME)-$(VERSION) && tar xf - ) - tar -cf ../$(PACKAGE_NAME)_$(VERSION).orig.tar $(PACKAGE_NAME)-$(VERSION) - $(RM) -r $(PACKAGE_NAME)-$(VERSION) - xz ../$(PACKAGE_NAME)_$(VERSION).orig.tar + mkdir -p $(PACKAGE_NAME)-$(VERSION) + tar --exclude=ax_lua.m4 --exclude=debian --exclude=\.pc \ + --exclude=$(PACKAGE_NAME)-$(VERSION) -cf - . \ + | ( cd $(PACKAGE_NAME)-$(VERSION) && tar xf - ) + tar -cf ../$(PACKAGE_NAME)_$(VERSION).orig.tar $(PACKAGE_NAME)-$(VERSION) + $(RM) -r $(PACKAGE_NAME)-$(VERSION) + xz ../$(PACKAGE_NAME)_$(VERSION).orig.tar \ No newline at end of file diff --git a/debian/telegram-cli.8 b/debian/telegram-cli.8 new file mode 100644 index 0000000..d0c29a0 --- /dev/null +++ b/debian/telegram-cli.8 @@ -0,0 +1,48 @@ +.TH man 8 "02 October 2014" "1.0.5.1" "telegram-cli man page" +.SH NAME +telegram-cli \- Command-line interface for Telegram messenger +.SH SYNOPSIS +telegram-cli -k [keyfile] +.SH DESCRIPTION + Telegram messenger is a cloud-based instant messaging designed for + smart phones and similar to Whatsapp but more flexible, and + powerful. You can send messages, photos, videos and documents to + people who are in your phone contacts (and have Telegram). Telegram + also supports secret chats whose provide a private (encrypted) way of + communication. + . + This package contains a command-line based client for Telegram with + the following features: + * Colored terminal messages. + * Message management: history, stats, etc. + * Group chat: create and manage groups. + * Secret chat: secured and encrypted conversations. + * Contact management: add/edit/remove contacts. + * Multimedia support: send/load photos and videos. +.SH OPTIONS +Telegram-cli Usage + -u specify username (would not be asked during authorization) + -k specify location of public key (possible multiple entries) + -v increase verbosity (0-ERROR 1-WARNIN 2-NOTICE 3+-DEBUG-levels) + -N message num mode + -c config file name + -p use specified profile + -l log level + -f during authorization fetch all messages since registration + -E diable auto accept of encrypted chats + -s lua script file + -W send dialog_list query and wait for answer before reading input + -C disable color output + -R disable readline + -d daemon mode + -L log file name + -U change uid after start + -G change gid after start + -D disable output + -P port to listen for input commands + -S unix socket to create +.SH SEE ALSO +.SH BUGS +No known bugs. +.SH AUTHOR +Vitaliy Vatman (-@-) \ No newline at end of file diff --git a/debian/telegram-cli.manpages b/debian/telegram-cli.manpages new file mode 100644 index 0000000..dc36f16 --- /dev/null +++ b/debian/telegram-cli.manpages @@ -0,0 +1 @@ +debian/telegram-cli.8 diff --git a/dump-tl-file.c b/dump-tl-file.c new file mode 100644 index 0000000..541a688 --- /dev/null +++ b/dump-tl-file.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "auto.h" + +int *tgl_in_ptr, *tgl_in_end; + +int tgl_packet_buffer[0]; +int *tgl_packet_ptr; + +char *tgl_strdup (char *s) { + return strdup (s); +} + +void tgl_out_cstring (const char *str, long len) {} +char *tglf_extf_fetch (struct paramed_type *T); + +#define LEN (1 << 28) +static int x[LEN / 4]; +int main (int argc, char **argv) { + int i; + int dump_binlog = 0; + while ((i = getopt (argc, argv, "b")) != -1) { + switch (i) { + case 'b': + dump_binlog = 1; + break; + default: + printf ("unknown option '%c'\n", (char)i); + exit (1); + } + } + if (!dump_binlog) { + exit (1); + } + if (optind + 1 != argc) { + exit (1); + } + int fd = open (argv[optind], O_RDONLY); + if (fd < 0) { + perror ("open"); + exit (1); + } + int r = read (fd, x, LEN); + if (r <= 0) { + perror ("read"); + exit (1); + } + if (r == LEN) { + printf ("Too long file\n"); + exit (1); + } + assert (!(r & 3)); + tgl_in_ptr = x; + tgl_in_end = x + (r / 4); + while (tgl_in_ptr < tgl_in_end) { + if (dump_binlog) { + char *R = tglf_extf_fetch (TYPE_TO_PARAM(binlog_update)); + if (!R) { + printf ("Can not fetch\n"); + exit (1); + } + printf ("%s\n", R); + } + } + return 0; +} diff --git a/encrypted_scheme.tl b/encrypted_scheme.tl index 1d76d0e..b45af3d 120000 --- a/encrypted_scheme.tl +++ b/encrypted_scheme.tl @@ -1 +1 @@ -encrypted_scheme16.tl \ No newline at end of file +encrypted_scheme17.tl \ No newline at end of file diff --git a/encrypted_scheme17.tl b/encrypted_scheme17.tl new file mode 100644 index 0000000..6418566 --- /dev/null +++ b/encrypted_scheme17.tl @@ -0,0 +1,30 @@ +---types--- +decryptedMessageLayer#99a438cf layer:int message:DecryptedMessage = DecryptedMessageLayer; +decryptedMessage_l16#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage; +decryptedMessageService_l16#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage; + +decryptedMessage#20a6d4e2 in_seq_no:int out_seq_no:int ttl:int random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage; +decryptedMessageService#9bb84a4e in_seq_no:int out_seq_no:int random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage; + +decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia; +decryptedMessageMediaPhoto#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia; +//decryptedMessageMediaVideo#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia; +decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia; +decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia; +decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction; + +decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia; +//decryptedMessageMediaAudio#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia; + +decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia; +decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia; +decryptedMessageActionReadMessages#c4f40be random_ids:Vector = DecryptedMessageAction; +decryptedMessageActionDeleteMessages#65614304 random_ids:Vector = DecryptedMessageAction; +decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector = DecryptedMessageAction; +decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction; +decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction; + + +decryptedMessageActionTyping#ccb27641 action:SendMessageAction = DecryptedMessageAction; + +---functions--- diff --git a/interface.c b/interface.c index 4ebfefa..9ed2adf 100644 --- a/interface.c +++ b/interface.c @@ -292,6 +292,23 @@ long long cur_token_int (void) { } } +double cur_token_double (void) { + if (cur_token_len <= 0) { + return NOT_FOUND; + } else { + char c = cur_token[cur_token_len]; + cur_token[cur_token_len] = 0; + char *end = 0; + double x = strtod (cur_token, &end); + cur_token[cur_token_len] = c; + if (end != cur_token + cur_token_len) { + return NOT_FOUND; + } else { + return x; + } + } +} + tgl_peer_id_t cur_token_user (void) { if (cur_token_len <= 0) { return TGL_PEER_NOT_FOUND; } int l = cur_token_len; @@ -300,17 +317,17 @@ tgl_peer_id_t cur_token_user (void) { char c = cur_token[cur_token_len]; cur_token[cur_token_len] = 0; - if (l >= 6 && !memcmp (s, "user#", 5)) { - s += 5; - l -= 5; + if (l >= 8 && !memcmp (s, "user#id", 7)) { + s += 7; + l -= 7; int r = atoi (s); cur_token[cur_token_len] = c; if (r >= 0) { return tgl_set_peer_id (TGL_PEER_USER, r); } else { return TGL_PEER_NOT_FOUND; } } - if (l >= 8 && !memcmp (s, "user#id", 7)) { - s += 7; - l -= 7; + if (l >= 6 && !memcmp (s, "user#", 5)) { + s += 5; + l -= 5; int r = atoi (s); cur_token[cur_token_len] = c; if (r >= 0) { return tgl_set_peer_id (TGL_PEER_USER, r); } @@ -335,18 +352,17 @@ tgl_peer_id_t cur_token_chat (void) { char c = cur_token[cur_token_len]; cur_token[cur_token_len] = 0; - if (l >= 6 && !memcmp (s, "chat#", 5)) { - s += 5; - l -= 5; + if (l >= 8 && !memcmp (s, "chat#id", 7)) { + s += 7; + l -= 7; int r = atoi (s); cur_token[cur_token_len] = c; if (r >= 0) { return tgl_set_peer_id (TGL_PEER_CHAT, r); } else { return TGL_PEER_NOT_FOUND; } } - - if (l >= 8 && !memcmp (s, "chat#id", 7)) { - s += 7; - l -= 7; + if (l >= 6 && !memcmp (s, "chat#", 5)) { + s += 5; + l -= 5; int r = atoi (s); cur_token[cur_token_len] = c; if (r >= 0) { return tgl_set_peer_id (TGL_PEER_CHAT, r); } @@ -386,22 +402,6 @@ tgl_peer_id_t cur_token_peer (void) { char c = cur_token[cur_token_len]; cur_token[cur_token_len] = 0; - if (l >= 6 && !memcmp (s, "user#", 5)) { - s += 5; - l -= 5; - int r = atoi (s); - cur_token[cur_token_len] = c; - if (r >= 0) { return tgl_set_peer_id (TGL_PEER_USER, r); } - else { return TGL_PEER_NOT_FOUND; } - } - if (l >= 6 && !memcmp (s, "chat#", 5)) { - s += 5; - l -= 5; - int r = atoi (s); - cur_token[cur_token_len] = c; - if (r >= 0) { return tgl_set_peer_id (TGL_PEER_CHAT, r); } - else { return TGL_PEER_NOT_FOUND; } - } if (l >= 8 && !memcmp (s, "user#id", 7)) { s += 7; l -= 7; @@ -418,6 +418,22 @@ tgl_peer_id_t cur_token_peer (void) { if (r >= 0) { return tgl_set_peer_id (TGL_PEER_CHAT, r); } else { return TGL_PEER_NOT_FOUND; } } + if (l >= 6 && !memcmp (s, "user#", 5)) { + s += 5; + l -= 5; + int r = atoi (s); + cur_token[cur_token_len] = c; + if (r >= 0) { return tgl_set_peer_id (TGL_PEER_USER, r); } + else { return TGL_PEER_NOT_FOUND; } + } + if (l >= 6 && !memcmp (s, "chat#", 5)) { + s += 5; + l -= 5; + int r = atoi (s); + cur_token[cur_token_len] = c; + if (r >= 0) { return tgl_set_peer_id (TGL_PEER_CHAT, r); } + else { return TGL_PEER_NOT_FOUND; } + } tgl_peer_t *P = tgl_peer_get_by_name (s); cur_token[cur_token_len] = c; @@ -432,6 +448,15 @@ tgl_peer_id_t cur_token_peer (void) { static tgl_peer_t *mk_peer (tgl_peer_id_t id) { if (tgl_get_peer_type (id) == NOT_FOUND) { return 0; } tgl_peer_t *P = tgl_peer_get (id); + if (!P) { + if (tgl_get_peer_type (id) == TGL_PEER_USER) { + tgl_insert_empty_user (tgl_get_peer_id (id)); + } + if (tgl_get_peer_type (id) == TGL_PEER_CHAT) { + tgl_insert_empty_chat (tgl_get_peer_id (id)); + } + P = tgl_peer_get (id); + } return P; } @@ -511,6 +536,7 @@ enum command_argument { ca_file_name_end, ca_period, ca_number, + ca_double, ca_string_end, ca_string, ca_modifier, @@ -528,6 +554,7 @@ struct arg { struct tgl_message *M; char *str; long long num; + double dval; }; }; @@ -988,6 +1015,11 @@ void do_clear (int arg_num, struct arg args[], struct in_ev *ev) { do_halt (0); } +void do_send_location (int arg_num, struct arg args[], struct in_ev *ev) { + assert (arg_num == 3); + tgl_do_send_location (args[0].P->id, args[1].dval, args[2].dval, 0, 0); +} + struct command commands[] = { {"help", {ca_none}, do_help, "help\tPrints this help"}, @@ -1030,7 +1062,7 @@ struct command commands[] = { {"chat_del_user", {ca_chat, ca_user, ca_none}, do_chat_del_user, "chat_del_user \tDeletes user from chat"}, {"status_online", {ca_none}, do_status_online, "status_online\tSets status as online"}, {"status_offline", {ca_none}, do_status_offline, "status_offline\tSets status as offline"}, - {"quit", {ca_none}, do_quit, "quit\tQuits immideatly"}, + {"quit", {ca_none}, do_quit, "quit\tQuits immediately"}, {"safe_quit", {ca_none}, do_safe_quit, "safe_quit\tWaits for all queries to end, then quits"}, {"set", {ca_string, ca_number, ca_none}, do_set, "set \tSets value of param. Currently available: log_level, debug_verbosity, alarm, msg_num"}, {"chat_with_peer", {ca_peer, ca_none}, do_chat_with_peer, "chat_with_peer \tInterface option. All input will be treated as messages to this peer. Type /quit to end this mode"}, @@ -1046,6 +1078,7 @@ struct command commands[] = { {"send_contact", {ca_peer, ca_string, ca_string, ca_string, ca_none}, do_send_contact, "send_contact \tSends contact (not necessary telegram user)"}, {"main_session", {ca_none}, do_main_session, "main_session\tSends updates to this connection (or terminal). Useful only with listening socket"}, {"clear", {ca_none}, do_clear, "clear\tClears all data and exits. For debug."}, + {"send_location", {ca_peer, ca_double, ca_double, ca_none}, do_send_location, "send_location \tSends geo location"}, {0, {ca_none}, 0, ""} }; @@ -1108,7 +1141,7 @@ enum command_argument get_complete_mode (void) { char *save = line_ptr; next_token (); - if (op == ca_user || op == ca_chat || op == ca_secret_chat || op == ca_peer || op == ca_number) { + if (op == ca_user || op == ca_chat || op == ca_secret_chat || op == ca_peer || op == ca_number || op == ca_double) { if (cur_token_quoted) { if (opt) { line_ptr = save; @@ -1137,6 +1170,9 @@ enum command_argument get_complete_mode (void) { case ca_number: ok = (cur_token_int () != NOT_FOUND); break; + case ca_double: + ok = (cur_token_double () != NOT_FOUND); + break; default: assert (0); } @@ -1222,7 +1258,7 @@ char *command_generator (const char *text, int state) { if (index == -1) { return 0; } } - if (mode == ca_none || mode == ca_string || mode == ca_string_end || mode == ca_number) { + if (mode == ca_none || mode == ca_string || mode == ca_string_end || mode == ca_number || mode == ca_double) { if (c) { rl_line_buffer[rl_point] = c; } return 0; } @@ -1594,26 +1630,68 @@ void mark_read_upd (int num, struct tgl_message *list[]) { } } -void type_notification_upd (struct tgl_user *U) { +void print_typing (struct in_ev *ev, enum tgl_typing_status status) { + switch (status) { + case tgl_typing_none: + mprintf (ev, "doing nothing"); + break; + case tgl_typing_typing: + mprintf (ev, "typing"); + break; + case tgl_typing_cancel: + mprintf (ev, "deleting typed message"); + break; + case tgl_typing_record_video: + mprintf (ev, "recording video"); + break; + case tgl_typing_upload_video: + mprintf (ev, "uploading video"); + break; + case tgl_typing_record_audio: + mprintf (ev, "recording audio"); + break; + case tgl_typing_upload_audio: + mprintf (ev, "uploading audio"); + break; + case tgl_typing_upload_photo: + mprintf (ev, "uploading photo"); + break; + case tgl_typing_upload_document: + mprintf (ev, "uploading document"); + break; + case tgl_typing_geo: + mprintf (ev, "choosing location"); + break; + case tgl_typing_choose_contact: + mprintf (ev, "choosing contact"); + break; + } +} + +void type_notification_upd (struct tgl_user *U, enum tgl_typing_status status) { if (log_level < 2 || (disable_output && !notify_ev)) { return; } struct in_ev *ev = notify_ev; mprint_start (ev); mpush_color (ev, COLOR_YELLOW); mprintf (ev, "User "); print_user_name (ev, U->id, (void *)U); - mprintf (ev, " is typing\n"); + mprintf (ev, " is "); + print_typing (ev, status); + mprintf (ev, "\n"); mpop_color (ev); mprint_end (ev); } -void type_in_chat_notification_upd (struct tgl_user *U, struct tgl_chat *C) { +void type_in_chat_notification_upd (struct tgl_user *U, struct tgl_chat *C, enum tgl_typing_status status) { if (log_level < 2 || (disable_output && !notify_ev)) { return; } struct in_ev *ev = notify_ev; mprint_start (ev); mpush_color (ev, COLOR_YELLOW); mprintf (ev, "User "); print_user_name (ev, U->id, (void *)U); - mprintf (ev, " is typing in chat "); + mprintf (ev, " is "); + print_typing (ev, status); + mprintf (ev, " in chat "); print_chat_name (ev, C->id, (void *)C); mprintf (ev, "\n"); mpop_color (ev); @@ -1916,7 +1994,10 @@ void interpreter_ex (char *line UU, void *ex) { if (op == ca_none) { next_token (); if (cur_token_end_str) { - fun (args_num, args, ex); + int z; + for (z = 0; z < count; z ++) { + fun (args_num, args, ex); + } } break; } @@ -1928,7 +2009,10 @@ void interpreter_ex (char *line UU, void *ex) { } else { args[args_num].flags = 1; args[args_num ++].str = strndup (cur_token, cur_token_len); - fun (args_num, args, ex); + int z; + for (z = 0; z < count; z ++) { + fun (args_num, args, ex); + } break; } } @@ -1937,17 +2021,24 @@ void interpreter_ex (char *line UU, void *ex) { next_token (); if (period && cur_token_end_str) { - fun (args_num, args, ex); + int z; + for (z = 0; z < count; z ++) { + fun (args_num, args, ex); + } break; } - if (op == ca_user || op == ca_chat || op == ca_secret_chat || op == ca_peer || op == ca_number) { + if (op == ca_user || op == ca_chat || op == ca_secret_chat || op == ca_peer || op == ca_number || op == ca_double) { if (cur_token_quoted) { if (opt) { - if (op != ca_number) { + if (op != ca_number && op != ca_double) { args[args_num ++].P = 0; } else { - args[args_num ++].num = NOT_FOUND; + if (op == ca_number) { + args[args_num ++].num = NOT_FOUND; + } else { + args[args_num ++].dval = NOT_FOUND; + } } line_ptr = save; flags ++; @@ -1958,10 +2049,14 @@ void interpreter_ex (char *line UU, void *ex) { } else { if (cur_token_end_str) { if (opt) { - if (op != ca_number) { + if (op != ca_number && op != ca_double) { args[args_num ++].P = 0; } else { - args[args_num ++].num = NOT_FOUND; + if (op == ca_number) { + args[args_num ++].num = NOT_FOUND; + } else { + args[args_num ++].dval = NOT_FOUND; + } } line_ptr = save; flags ++; @@ -1992,6 +2087,10 @@ void interpreter_ex (char *line UU, void *ex) { args[args_num ++].num = cur_token_int (); ok = (args[args_num - 1].num != NOT_FOUND); break; + case ca_double: + args[args_num ++].dval = cur_token_double (); + ok = (args[args_num - 1].dval != NOT_FOUND); + break; default: assert (0); } @@ -2386,6 +2485,10 @@ void print_service_message (struct in_ev *ev, struct tgl_message *M) { case tgl_message_action_notify_layer: mprintf (ev, " updated layer to %d\n", M->action.layer); break; + case tgl_message_action_typing: + mprintf (ev, " is "); + print_typing (ev, M->action.typing); + break; default: assert (0); } diff --git a/loop.c b/loop.c index f3ec7de..ee45225 100644 --- a/loop.c +++ b/loop.c @@ -91,6 +91,8 @@ static int delete_stdin_event; extern volatile int sigterm_cnt; +extern char *start_command; + static void stdin_read_callback_all (int arg, short what, struct event *self) { if (!readline_disabled) { if (((long)arg) & 1) { @@ -500,9 +502,19 @@ void read_dc (int auth_file_fd, int id, unsigned ver) { } void empty_auth_file (void) { - char *ip = tgl_state.test_mode ? TG_SERVER_TEST : TG_SERVER; - bl_do_dc_option (tgl_state.test_mode ? TG_SERVER_TEST_DC : TG_SERVER_DC, 0, "", strlen (ip), ip, 443); - bl_do_set_working_dc (tgl_state.test_mode ? TG_SERVER_TEST_DC : TG_SERVER_DC); + if (tgl_state.test_mode) { + bl_do_dc_option (1, 0, "", strlen (TG_SERVER_TEST_1), TG_SERVER_TEST_1, 443); + bl_do_dc_option (2, 0, "", strlen (TG_SERVER_TEST_2), TG_SERVER_TEST_2, 443); + bl_do_dc_option (3, 0, "", strlen (TG_SERVER_TEST_3), TG_SERVER_TEST_3, 443); + bl_do_set_working_dc (2); + } else { + bl_do_dc_option (1, 0, "", strlen (TG_SERVER_1), TG_SERVER_1, 443); + bl_do_dc_option (2, 0, "", strlen (TG_SERVER_2), TG_SERVER_2, 443); + bl_do_dc_option (3, 0, "", strlen (TG_SERVER_3), TG_SERVER_3, 443); + bl_do_dc_option (4, 0, "", strlen (TG_SERVER_4), TG_SERVER_4, 443); + bl_do_dc_option (5, 0, "", strlen (TG_SERVER_5), TG_SERVER_5, 443); + bl_do_set_working_dc (2); + } } int need_dc_list_update; @@ -842,6 +854,20 @@ int loop (void) { lua_diff_end (); #endif + if (start_command) { + safe_quit = 1; + while (*start_command) { + char *start = start_command; + while (*start_command && *start_command != '\n') { + start_command ++; + } + if (*start_command) { + *start_command = 0; + start_command ++; + } + interpreter_ex (start, 0); + } + } /*tgl_do_get_dialog_list (get_dialogs_callback, 0); if (wait_dialog_list) { diff --git a/lua-tg.c b/lua-tg.c index 7a63824..7d15c31 100644 --- a/lua-tg.c +++ b/lua-tg.c @@ -495,6 +495,7 @@ enum lua_query_type { lq_send_contact, lq_status_online, lq_status_offline, + lq_send_location, lq_extf }; @@ -1086,6 +1087,14 @@ void lua_do_all (void) { free (s); p += 2; break; + case lq_send_location: + if (sizeof (void *) == 4) { + tgl_do_send_location (((tgl_peer_t *)lua_ptr[p + 1])->id , *(float *)(lua_ptr + p + 2), *(float *)(lua_ptr + p + 3), lua_msg_cb, lua_ptr[p]); + } else { + tgl_do_send_location (((tgl_peer_t *)lua_ptr[p + 1])->id , *(double *)(lua_ptr + p + 2), *(double *)(lua_ptr + p + 3), lua_msg_cb, lua_ptr[p]); + } + p += 4; + break; /* lq_delete_msg, lq_restore_msg, @@ -1120,7 +1129,8 @@ enum lua_function_param { lfp_number, lfp_positive_number, lfp_nonnegative_number, - lfp_msg + lfp_msg, + lfp_double }; struct lua_function { @@ -1168,6 +1178,7 @@ struct lua_function functions[] = { {"send_contact", lq_send_contact, { lfp_peer, lfp_string, lfp_string, lfp_string, lfp_none }}, {"status_online", lq_status_online, { lfp_none }}, {"status_offline", lq_status_offline, { lfp_none }}, + {"send_location", lq_send_location, { lfp_peer, lfp_double, lfp_double, lfp_none }}, {"ext_function", lq_extf, { lfp_string, lfp_none }}, { 0, 0, { lfp_none}} }; @@ -1203,6 +1214,7 @@ static int parse_lua_function (lua_State *L, struct lua_function *F) { const char *s; tgl_peer_t *P; long long num; + double dval; struct tgl_message *M; switch (F->params[p]) { case lfp_none: @@ -1256,6 +1268,17 @@ static int parse_lua_function (lua_State *L, struct lua_function *F) { lua_ptr[pos + p] = (void *)(long)num; break; + case lfp_double: + dval = lua_tonumber (L, -cc); + + if (sizeof (void *) == 4) { + *(float *)(lua_ptr + pos + p) = dval; + } else { + assert (sizeof (void *) >= 8); + *(double *)(lua_ptr + pos + p) = dval; + } + break; + case lfp_positive_number: num = lua_tonumber (L, -cc); if (num <= 0) { diff --git a/main.c b/main.c index ff57ec1..4ac1aec 100644 --- a/main.c +++ b/main.c @@ -59,6 +59,7 @@ #include #include +#include #include "telegram.h" #include "loop.h" @@ -110,6 +111,7 @@ int readline_disabled; int disable_output; int reset_authorization; int port; +char *start_command; void set_default_username (const char *s) { if (default_username) { @@ -283,7 +285,7 @@ void parse_config_val (config_t *conf, char **s, char *param_name, const char *d strcpy (buf + l, param_name); config_lookup_string (conf, buf, &r); if (r) { - if (path) { + if (path && *r != '/') { tasprintf (s, "%s/%s", path, r); } else { *s = tstrdup (r); @@ -399,6 +401,11 @@ void parse_config (void) { tasprintf (&secret_chat_file_name, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, SECRET_CHAT_FILE); } tgl_set_download_directory (downloads_directory); + if (!mkdir (downloads_directory, CONFIG_DIRECTORY_MODE)) { + if (!disable_output) { + printf ("[%s] created\n", downloads_directory); + } + } } #endif @@ -421,7 +428,7 @@ void usage (void) { #endif printf (" -l log level\n"); printf (" -f during authorization fetch all messages since registration\n"); - printf (" -E diable auto accept of encrypted chats\n"); + printf (" -E disable auto accept of encrypted chats\n"); #ifdef USE_LUA printf (" -s lua script file\n"); #endif @@ -435,6 +442,7 @@ void usage (void) { printf (" -D disable output\n"); printf (" -P port to listen for input commands\n"); printf (" -S unix socket to create\n"); + printf (" -e make commands end exit\n"); exit (1); } @@ -537,7 +545,7 @@ char *unix_socket; void args_parse (int argc, char **argv) { int opt = 0; - while ((opt = getopt (argc, argv, "u:hk:vNl:fEwWCRdL:DU:G:qP:S:" + while ((opt = getopt (argc, argv, "u:hk:vNl:fEwWCRdL:DU:G:qP:S:e:" #ifdef HAVE_LIBCONFIG "c:p:" #else @@ -626,6 +634,9 @@ void args_parse (int argc, char **argv) { case 'S': unix_socket = optarg; break; + case 'e': + start_command = optarg; + break; case 'h': default: usage (); diff --git a/mtproto-client.c b/mtproto-client.c index 12ac336..1dab523 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -80,7 +80,7 @@ #define MAX_NET_RES (1L << 16) //extern int log_level; -#ifndef HAVE___BUILTIN_BSWAP32 +#if !defined(HAVE___BUILTIN_BSWAP32) && !defined(__FreeBSD__) && !defined(__OpenBSD__) static inline unsigned __builtin_bswap32(unsigned x) { return ((x << 24) & 0xff000000 ) | ((x << 8) & 0x00ff0000 ) | @@ -284,6 +284,13 @@ static int process_respq_answer (struct connection *c, char *packet, int len, in unsigned long long what; unsigned p1, p2; int i; + + long long packet_auth_key_id = *(long long *)packet; + if (packet_auth_key_id) { + assert (temp_key); + vlogprintf (E_WARNING, "received packet during creation of temp auth key. Probably answer on old query. Drop\n"); + return 0; + } vlogprintf (E_DEBUG, "process_respq_answer(), len=%d, op=0x%08x\n", len, *(int *)(packet + 20)); assert (len >= 76); assert (!*(long long *) packet); @@ -552,6 +559,12 @@ static int process_dh_answer (struct connection *c, char *packet, int len, int t //if (len < 116) { // vlogprintf (E_ERROR, "%u * %u = %llu", p1, p2, what); //} + long long packet_auth_key_id = *(long long *)packet; + if (packet_auth_key_id) { + assert (temp_key); + vlogprintf (E_WARNING, "received packet during creation of temp auth key. Probably answer on old query. Drop\n"); + return 0; + } assert (len >= 116); assert (!*(long long *) packet); assert (*(int *) (packet + 16) == len - 20); @@ -660,6 +673,13 @@ static void mpc_on_get_config (void *extra, int success); static int process_auth_complete (struct connection *c UU, char *packet, int len, int temp_key) { struct tgl_dc *D = tgl_state.net_methods->get_dc (c); vlogprintf (E_DEBUG - 1, "process_dh_answer(), len=%d\n", len); + + long long packet_auth_key_id = *(long long *)packet; + if (packet_auth_key_id) { + assert (temp_key); + vlogprintf (E_WARNING, "received packet during creation of temp auth key. Probably answer on old query. Drop\n"); + return 0; + } assert (len == 72); assert (!*(long long *) packet); assert (*(int *) (packet + 16) == len - 20); @@ -1058,8 +1078,9 @@ static int process_rpc_message (struct connection *c UU, struct encrypted_messag assert (len >= MINSZ && (len & 15) == (UNENCSZ & 15)); struct tgl_dc *DC = tgl_state.net_methods->get_dc (c); if (enc->auth_key_id != DC->temp_auth_key_id && enc->auth_key_id != DC->auth_key_id) { - vlogprintf (E_ERROR, "received msg from dc %d with auth_key_id %lld (perm_auth_key_id %lld temp_auth_key_id %lld)\n", + vlogprintf (E_WARNING, "received msg from dc %d with auth_key_id %lld (perm_auth_key_id %lld temp_auth_key_id %lld). Dropping\n", DC->id, enc->auth_key_id, DC->auth_key_id, DC->temp_auth_key_id); + return 0; } if (enc->auth_key_id == DC->temp_auth_key_id) { assert (enc->auth_key_id == DC->temp_auth_key_id); @@ -1280,7 +1301,7 @@ void tglmp_on_start (void) { } if (!ok) { - vlogprintf (E_ERROR, "No pubic keys found\n"); + vlogprintf (E_ERROR, "No public keys found\n"); exit (1); } @@ -1341,19 +1362,26 @@ static void regen_temp_key_gw (evutil_socket_t fd, short what, void *arg) { } struct tgl_dc *tglmp_alloc_dc (int id, char *ip, int port UU) { - assert (!tgl_state.DC_list[id]); - struct tgl_dc *DC = talloc0 (sizeof (*DC)); - DC->id = id; - DC->ip = ip; - DC->port = port; - tgl_state.DC_list[id] = DC; - if (id > tgl_state.max_dc_num) { - tgl_state.max_dc_num = id; + //assert (!tgl_state.DC_list[id]); + if (!tgl_state.DC_list[id]) { + struct tgl_dc *DC = talloc0 (sizeof (*DC)); + DC->id = id; + DC->ip = ip; + DC->port = port; + tgl_state.DC_list[id] = DC; + if (id > tgl_state.max_dc_num) { + tgl_state.max_dc_num = id; + } + DC->ev = evtimer_new (tgl_state.ev_base, regen_temp_key_gw, DC); + static struct timeval p; + event_add (DC->ev, &p); + return DC; + } else { + struct tgl_dc *DC = tgl_state.DC_list[id]; + tfree_str (DC->ip); + DC->ip = tstrdup (ip); + return DC; } - DC->ev = evtimer_new (tgl_state.ev_base, regen_temp_key_gw, DC); - static struct timeval p; - event_add (DC->ev, &p); - return DC; } static struct mtproto_methods mtproto_methods = { diff --git a/mtproto-common.h b/mtproto-common.h index 2ef2aae..731486c 100644 --- a/mtproto-common.h +++ b/mtproto-common.h @@ -201,7 +201,6 @@ static inline int prefetch_strlen (void) { static inline char *fetch_str (int len) { assert (len >= 0); - vlogprintf (E_DEBUG + 3, "fetch_string: len = %d\n", len); if (len < 254) { char *str = (char *) in_ptr + 1; in_ptr += 1 + (len >> 2); @@ -294,12 +293,10 @@ int tgl_fetch_bignum (BIGNUM *x); static inline int fetch_int (void) { assert (in_ptr + 1 <= in_end); - vlogprintf (E_DEBUG + 3, "fetch_int: 0x%08x (%d)\n", *in_ptr, *in_ptr); return *(in_ptr ++); } static inline int fetch_bool (void) { - vlogprintf (E_DEBUG + 3, "fetch_bool: 0x%08x (%d)\n", *in_ptr, *in_ptr); assert (in_ptr + 1 <= in_end); assert (*(in_ptr) == (int)CODE_bool_true || *(in_ptr) == (int)CODE_bool_false); return *(in_ptr ++) == (int)CODE_bool_true; diff --git a/net.c b/net.c index dd3c24e..b8af790 100644 --- a/net.c +++ b/net.c @@ -26,11 +26,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -345,6 +345,10 @@ static void restart_connection (struct connection *c) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons (c->port); + if (strcmp (c->ip, c->dc->ip)) { + tfree_str (c->ip); + c->ip = tstrdup (c->dc->ip); + } addr.sin_addr.s_addr = inet_addr (c->ip); diff --git a/queries.c b/queries.c index 4cbc740..a72aa46 100644 --- a/queries.c +++ b/queries.c @@ -94,7 +94,7 @@ struct query *tglq_query_get (long long id) { static int alarm_query (struct query *q) { assert (q); - vlogprintf (E_DEBUG, "Alarm query %lld\n", q->msg_id); + vlogprintf (E_DEBUG - 1, "Alarm query %lld\n", q->msg_id); //q->ev.timeout = get_double_time () + QUERY_TIMEOUT; //insert_event_timer (&q->ev); @@ -118,7 +118,9 @@ static int alarm_query (struct query *q) { tglmp_encrypt_send_message (q->session->c, packet_buffer, packet_ptr - packet_buffer, q->flags & QUERY_FORCE_SEND); } else { q->flags &= ~QUERY_ACK_RECEIVED; - queries_tree = tree_delete_query (queries_tree, q); + if (tree_lookup_query (queries_tree, q)) { + queries_tree = tree_delete_query (queries_tree, q); + } q->msg_id = tglmp_encrypt_send_message (q->session->c, q->data, q->data_len, (q->flags & QUERY_FORCE_SEND) | 1); queries_tree = tree_insert_query (queries_tree, q, lrand48 ()); q->session_id = q->session->session_id; @@ -219,14 +221,35 @@ void tglq_query_error (long long id) { event_del (q->ev); } queries_tree = tree_delete_query (queries_tree, q); - if (q->methods && q->methods->on_error) { - q->methods->on_error (q, error_code, error_len, error); + int res = 0; + if (q->methods && q->methods->on_error && error_code != 500) { + res = q->methods->on_error (q, error_code, error_len, error); } else { - vlogprintf ( E_WARNING, "error for query #%lld: #%d :%.*s\n", id, error_code, error_len, error); + if (error_code == 420 || error_code == 500) { + int wait; + if (error_code == 420) { + if (strncmp (error, "FLOOD_WAIT_", 11)) { + vlogprintf (E_ERROR, "error = '%s'\n", error); + wait = 10; + } else { + wait = atoll (error + 11); + } + } else { + wait = 10; + } + q->flags &= ~QUERY_ACK_RECEIVED; + static struct timeval ptimeout; + ptimeout.tv_sec = wait; + event_add (q->ev, &ptimeout); + q->session_id = 0; + res = 1; + } + } + if (res <= 0) { + tfree (q->data, q->data_len * 4); + event_free (q->ev); + tfree (q, sizeof (*q)); } - tfree (q->data, q->data_len * 4); - event_free (q->ev); - tfree (q, sizeof (*q)); } tgl_state.active_queries --; } @@ -314,7 +337,7 @@ static void out_random (int n) { int allow_send_linux_version; void tgl_do_insert_header (void) { - out_int (CODE_invoke_with_layer16); + out_int (CODE_invoke_with_layer17); out_int (CODE_init_connection); out_int (TG_APP_ID); if (allow_send_linux_version) { @@ -914,7 +937,17 @@ static int msg_send_on_answer (struct query *q UU) { } static int msg_send_on_error (struct query *q, int error_code, int error_len, char *error) { - vlogprintf (E_WARNING, "error for query #%lld: #%d :%.*s\n", q->msg_id, error_code, error_len, error); + //vlogprintf (E_WARNING, "error for query #%lld: #%d :%.*s\n", q->msg_id, error_code, error_len, error); + if (error_code == 420) { + assert (!strncmp (error, "FLOOD_WAIT_", 11)); + int wait = atoll (error + 11); + q->flags &= ~QUERY_ACK_RECEIVED; + static struct timeval ptimeout; + ptimeout.tv_sec = wait; + event_add (q->ev, &ptimeout); + q->session_id = 0; + return 1; + } long long x = *(long long *)q->extra; tfree (q->extra, 8); struct tgl_message *M = tgl_message_get (x); @@ -957,7 +990,13 @@ void tgl_do_send_encr_msg_action (struct tgl_message *M, void (*callback)(void * out_long (P->encr_chat.access_hash); out_long (M->id); encr_start (); - out_int (CODE_decrypted_message_service); + if (P->encr_chat.layer <= 16) { + out_int (CODE_decrypted_message_service_l16); + } else { + out_int (CODE_decrypted_message_service); + out_int (2 * P->encr_chat.in_seq_no + (P->encr_chat.admin_id != tgl_state.our_id)); + out_int (2 * P->encr_chat.out_seq_no + (P->encr_chat.admin_id == tgl_state.our_id)); + } out_long (M->id); static int buf[4]; tglt_secure_random (buf, 16); @@ -1001,7 +1040,14 @@ void tgl_do_send_encr_msg (struct tgl_message *M, void (*callback)(void *callbac out_long (P->encr_chat.access_hash); out_long (M->id); encr_start (); - out_int (CODE_decrypted_message); + if (P->encr_chat.layer <= 16) { + out_int (CODE_decrypted_message_l16); + } else { + out_int (CODE_decrypted_message); + out_int (2 * P->encr_chat.in_seq_no + (P->encr_chat.admin_id != tgl_state.our_id)); + out_int (2 * P->encr_chat.out_seq_no + (P->encr_chat.admin_id == tgl_state.our_id)); + out_int (0); + } out_long (M->id); static int buf[4]; tglt_secure_random (buf, 16); @@ -1141,6 +1187,7 @@ void tgl_do_messages_mark_read (tgl_peer_id_t id, int max_id, int offset, void ( out_peer_id (id); out_int (max_id); out_int (offset); + out_int (CODE_bool_true); int *t = talloc (12); t[0] = tgl_get_peer_type (id); t[1] = tgl_get_peer_id (id); @@ -1738,7 +1785,14 @@ static void send_part (struct send_file *f, void *callback, void *callback_extra tglt_secure_random (&r, 8); out_long (r); encr_start (); - out_int (CODE_decrypted_message); + if (P->encr_chat.layer <= 16) { + out_int (CODE_decrypted_message_l16); + } else { + out_int (CODE_decrypted_message); + out_int (2 * P->encr_chat.in_seq_no + (P->encr_chat.admin_id != tgl_state.our_id)); + out_int (2 * P->encr_chat.out_seq_no + (P->encr_chat.admin_id == tgl_state.our_id) + 2); + out_int (0); + } out_long (r); out_random (15 + 4 * (lrand48 () % 3)); out_string (""); @@ -1783,9 +1837,7 @@ static void send_part (struct send_file *f, void *callback, void *callback_extra out_cstring ((void *)f->key, 32); out_cstring ((void *)f->init_iv, 32); - long long msg_id; - tglt_secure_random (&msg_id, 8); - bl_do_create_message_media_encr_pending (msg_id, tgl_state.our_id, tgl_get_peer_type (f->to_id), tgl_get_peer_id (f->to_id), time (0), 0, 0, save_ptr, packet_ptr - save_ptr); + bl_do_create_message_media_encr_pending (r, tgl_state.our_id, tgl_get_peer_type (f->to_id), tgl_get_peer_id (f->to_id), time (0), 0, 0, save_ptr, packet_ptr - save_ptr); encr_finish (&P->encr_chat); if (f->size < (16 << 20)) { @@ -1807,7 +1859,7 @@ static void send_part (struct send_file *f, void *callback, void *callback_extra out_int ((*(int *)md5) ^ (*(int *)(md5 + 4))); tfree_secure (f->iv, 32); - struct tgl_message *M = tgl_message_get (msg_id); + struct tgl_message *M = tgl_message_get (r); assert (M); //M->media.encr_photo.key = f->key; @@ -1943,6 +1995,7 @@ void tgl_do_set_profile_photo (char *file_name, void (*callback)(void *callback_ } /* }}} */ + /* {{{ Forward */ static int fwd_msg_on_answer (struct query *q UU) { assert (fetch_int () == (int)CODE_messages_stated_message); @@ -2093,6 +2146,65 @@ void tgl_do_forward_media (tgl_peer_id_t id, int n, void (*callback)(void *callb } /* }}} */ +/* {{{ Send location */ + +void tgl_do_send_location(tgl_peer_id_t id, double latitude, double longitude, void (*callback)(void *callback_extra, int success, struct tgl_message *M), void *callback_extra) { + if (tgl_get_peer_type (id) == TGL_PEER_ENCR_CHAT) { + clear_packet (); + out_int (CODE_messages_send_encrypted); + out_int (CODE_input_encrypted_chat); + out_int (tgl_get_peer_id (id)); + tgl_peer_t *P = tgl_peer_get (id); + assert (P); + out_long (P->encr_chat.access_hash); + + long long r; + tglt_secure_random (&r, 8); + out_long (r); + encr_start (); + if (P->encr_chat.layer <= 16) { + out_int (CODE_decrypted_message_l16); + } else { + out_int (CODE_decrypted_message); + out_int (2 * P->encr_chat.in_seq_no + (P->encr_chat.admin_id != tgl_state.our_id)); + out_int (2 * P->encr_chat.out_seq_no + (P->encr_chat.admin_id == tgl_state.our_id) + 2); + out_int (0); + } + out_long (r); + out_random (15 + 4 * (lrand48 () % 3)); + out_string (""); + int *save_ptr = packet_ptr; + out_int (CODE_decrypted_message_media_geo_point); + out_double (latitude); + out_double (longitude); + + bl_do_create_message_media_encr_pending (r, tgl_state.our_id, tgl_get_peer_type (id), tgl_get_peer_id (id), time (0), 0, 0, save_ptr, packet_ptr - save_ptr); + + encr_finish (&P->encr_chat); + + struct tgl_message *M = tgl_message_get (r); + assert (M); + + tglq_send_query (tgl_state.DC_working, packet_ptr - packet_buffer, packet_buffer, &msg_send_encr_methods, M, callback, callback_extra); + } else { + long long t; + tglt_secure_random (&t, 8); + vlogprintf (E_DEBUG, "t = %lld\n", t); + + clear_packet (); + out_int (CODE_messages_send_media); + out_peer_id (id); + out_int (CODE_input_media_geo_point); + out_int (CODE_input_geo_point); + out_double (latitude); + out_double (longitude); + out_long (t); + + tglq_send_query (tgl_state.DC_working, packet_ptr - packet_buffer, packet_buffer, &fwd_msg_methods, 0, callback, callback_extra); + } +} +/* }}} */ + /* {{{ Rename chat */ static int rename_chat_on_answer (struct query *q UU) { assert (fetch_int () == (int)CODE_messages_stated_message); @@ -2361,6 +2473,10 @@ static int download_on_answer (struct query *q) { struct download *D = q->extra; if (D->fd == -1) { D->fd = open (D->name, O_CREAT | O_WRONLY, 0640); + if (D->fd < 0) { + vlogprintf (E_ERROR, "Can not open for writing: %m\n"); + assert (D->fd >= 0); + } } fetch_int (); // mtime int len = prefetch_strlen (); @@ -2906,6 +3022,9 @@ static int send_encr_accept_on_answer (struct query *q UU) { print_end (); }*/ + if (E->state == sc_ok) { + tgl_do_send_encr_chat_layer (E); + } if (q->callback) { ((void (*)(void *, int, struct tgl_secret_chat *))q->callback) (q->callback_extra, E->state == sc_ok, E); } diff --git a/scheme.tl b/scheme.tl index ca97beb..be87a03 120000 --- a/scheme.tl +++ b/scheme.tl @@ -1 +1 @@ -scheme16.tl \ No newline at end of file +scheme17.tl \ No newline at end of file diff --git a/scheme17.tl b/scheme17.tl new file mode 100644 index 0000000..c564155 --- /dev/null +++ b/scheme17.tl @@ -0,0 +1,528 @@ +int ?= Int; +long ?= Long; +double ?= Double; +string ?= String; + +bytes string = Bytes; + +boolFalse#bc799737 = Bool; +boolTrue#997275b5 = Bool; + +vector#1cb5c415 {t:Type} # [ t ] = Vector t; + +error#c4b9f9bb code:int text:string = Error; + +null#56730bcc = Null; + +inputPeerEmpty#7f3b18ea = InputPeer; +inputPeerSelf#7da07ec9 = InputPeer; +inputPeerContact#1023dbe8 user_id:int = InputPeer; +inputPeerForeign#9b447325 user_id:int access_hash:long = InputPeer; +inputPeerChat#179be863 chat_id:int = InputPeer; + +inputUserEmpty#b98886cf = InputUser; +inputUserSelf#f7c1b13f = InputUser; +inputUserContact#86e94f65 user_id:int = InputUser; +inputUserForeign#655e74ff user_id:int access_hash:long = InputUser; + +inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_name:string = InputContact; + +inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile; + +inputMediaEmpty#9664f57f = InputMedia; +inputMediaUploadedPhoto#2dc53a7d file:InputFile = InputMedia; +inputMediaPhoto#8f2ab2ec id:InputPhoto = InputMedia; +inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia; +inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia; +inputMediaUploadedVideo#133ad6f6 file:InputFile duration:int w:int h:int mime_type:string = InputMedia; +inputMediaUploadedThumbVideo#9912dabf file:InputFile thumb:InputFile duration:int w:int h:int mime_type:string = InputMedia; +inputMediaVideo#7f023ae6 id:InputVideo = InputMedia; + +inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; +inputChatUploadedPhoto#94254732 file:InputFile crop:InputPhotoCrop = InputChatPhoto; +inputChatPhoto#b2e1bf08 id:InputPhoto crop:InputPhotoCrop = InputChatPhoto; + +inputGeoPointEmpty#e4c123d6 = InputGeoPoint; +inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint; + +inputPhotoEmpty#1cd7bf0d = InputPhoto; +inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto; + +inputVideoEmpty#5508ec75 = InputVideo; +inputVideo#ee579652 id:long access_hash:long = InputVideo; + +inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation; +inputVideoFileLocation#3d0364ec id:long access_hash:long = InputFileLocation; + +inputPhotoCropAuto#ade6b004 = InputPhotoCrop; +inputPhotoCrop#d9915325 crop_left:double crop_top:double crop_width:double = InputPhotoCrop; + +inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent; + +peerUser#9db1bc6d user_id:int = Peer; +peerChat#bad0e5bb chat_id:int = Peer; + +storage.fileUnknown#aa963b05 = storage.FileType; +storage.fileJpeg#7efe0e = storage.FileType; +storage.fileGif#cae1aadf = storage.FileType; +storage.filePng#a4f63c0 = storage.FileType; +storage.filePdf#ae1e508d = storage.FileType; +storage.fileMp3#528a0677 = storage.FileType; +storage.fileMov#4b09ebbc = storage.FileType; +storage.filePartial#40bc6f52 = storage.FileType; +storage.fileMp4#b3cea0e4 = storage.FileType; +storage.fileWebp#1081464c = storage.FileType; + +fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation; +fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation; + +userEmpty#200250ba id:int = User; +userSelf#720535ec id:int first_name:string last_name:string phone:string photo:UserProfilePhoto status:UserStatus inactive:Bool = User; +userContact#f2fb8319 id:int first_name:string last_name:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User; +userRequest#22e8ceb0 id:int first_name:string last_name:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User; +userForeign#5214c89d id:int first_name:string last_name:string access_hash:long photo:UserProfilePhoto status:UserStatus = User; +userDeleted#b29ad7cc id:int first_name:string last_name:string = User; + +userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; +userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; + +userStatusEmpty#9d05049 = UserStatus; +userStatusOnline#edb93949 expires:int = UserStatus; +userStatusOffline#8c703f was_online:int = UserStatus; + +chatEmpty#9ba2d800 id:int = Chat; +chat#6e9c9bc7 id:int title:string photo:ChatPhoto participants_count:int date:int left:Bool version:int = Chat; +chatForbidden#fb0ccc41 id:int title:string date:int = Chat; + +chatFull#630e61be id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings = ChatFull; + +chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; + +chatParticipantsForbidden#fd2bb8a chat_id:int = ChatParticipants; +chatParticipants#7841b415 chat_id:int admin_id:int participants:Vector version:int = ChatParticipants; + +chatPhotoEmpty#37c1011c = ChatPhoto; +chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto; + +messageEmpty#83e5de54 id:int = Message; +//message#22eb6aba id:int from_id:int to_id:Peer out:Bool unread:Bool date:int message:string media:MessageMedia = Message; +//messageForwarded#5f46804 id:int fwd_from_id:int fwd_date:int from_id:int to_id:Peer out:Bool unread:Bool date:int message:string media:MessageMedia = Message; +//messageService#9f8d60bb id:int from_id:int to_id:Peer out:Bool unread:Bool date:int action:MessageAction = Message; + +messageMediaEmpty#3ded6320 = MessageMedia; +messageMediaPhoto#c8c45a2a photo:Photo = MessageMedia; +messageMediaVideo#a2d24290 video:Video = MessageMedia; +messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia; +messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia; +messageMediaUnsupported#29632a36 bytes:bytes = MessageMedia; + +messageActionEmpty#b6aef7b0 = MessageAction; +messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction; +messageActionChatEditTitle#b5a1ce5a title:string = MessageAction; +messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction; +messageActionChatDeletePhoto#95e3fbef = MessageAction; +messageActionChatAddUser#5e3cfc4b user_id:int = MessageAction; +messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction; + +dialog#ab3a99ac peer:Peer top_message:int unread_count:int notify_settings:PeerNotifySettings = Dialog; + +photoEmpty#2331b22d id:long = Photo; +photo#22b56751 id:long access_hash:long user_id:int date:int caption:string geo:GeoPoint sizes:Vector = Photo; + +photoSizeEmpty#e17e23c type:string = PhotoSize; +photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; +photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; + +videoEmpty#c10658a8 id:long = Video; +video#388fa391 id:long access_hash:long user_id:int date:int caption:string duration:int mime_type:string size:int thumb:PhotoSize dc_id:int w:int h:int = Video; + +geoPointEmpty#1117dd5f = GeoPoint; +geoPoint#2049d70c long:double lat:double = GeoPoint; + +auth.checkedPhone#e300cc3b phone_registered:Bool phone_invited:Bool = auth.CheckedPhone; + +auth.sentCode#efed51d9 phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode; + +auth.authorization#f6b673a4 expires:int user:User = auth.Authorization; + +auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization; + +inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer; +inputNotifyUsers#193b4417 = InputNotifyPeer; +inputNotifyChats#4a95e84e = InputNotifyPeer; +inputNotifyAll#a429b886 = InputNotifyPeer; + +inputPeerNotifyEventsEmpty#f03064d8 = InputPeerNotifyEvents; +inputPeerNotifyEventsAll#e86a2c74 = InputPeerNotifyEvents; + +inputPeerNotifySettings#46a2ce98 mute_until:int sound:string show_previews:Bool events_mask:int = InputPeerNotifySettings; + +peerNotifyEventsEmpty#add53cb3 = PeerNotifyEvents; +peerNotifyEventsAll#6d1ded88 = PeerNotifyEvents; + +peerNotifySettingsEmpty#70a68512 = PeerNotifySettings; +peerNotifySettings#8d5e11ee mute_until:int sound:string show_previews:Bool events_mask:int = PeerNotifySettings; + +wallPaper#ccb03657 id:int title:string sizes:Vector color:int = WallPaper; + +userFull#771095da user:User link:contacts.Link profile_photo:Photo notify_settings:PeerNotifySettings blocked:Bool real_first_name:string real_last_name:string = UserFull; + +contact#f911c994 user_id:int mutual:Bool = Contact; + +importedContact#d0028438 user_id:int client_id:long = ImportedContact; + +contactBlocked#561bc879 user_id:int date:int = ContactBlocked; + +contactFound#ea879f95 user_id:int = ContactFound; + +contactSuggested#3de191a1 user_id:int mutual_contacts:int = ContactSuggested; + +contactStatus#aa77b873 user_id:int expires:int = ContactStatus; + +chatLocated#3631cf4c chat_id:int distance:int = ChatLocated; + +contacts.foreignLinkUnknown#133421f8 = contacts.ForeignLink; +contacts.foreignLinkRequested#a7801f47 has_phone:Bool = contacts.ForeignLink; +contacts.foreignLinkMutual#1bea8ce1 = contacts.ForeignLink; + +contacts.myLinkEmpty#d22a1c60 = contacts.MyLink; +contacts.myLinkRequested#6c69efee contact:Bool = contacts.MyLink; +contacts.myLinkContact#c240ebd9 = contacts.MyLink; + +contacts.link#eccea3f5 my_link:contacts.MyLink foreign_link:contacts.ForeignLink user:User = contacts.Link; + +contacts.contacts#6f8b8cb2 contacts:Vector users:Vector = contacts.Contacts; +contacts.contactsNotModified#b74ba9d2 = contacts.Contacts; + +contacts.importedContacts#ad524315 imported:Vector retry_contacts:Vector users:Vector = contacts.ImportedContacts; + +contacts.blocked#1c138d15 blocked:Vector users:Vector = contacts.Blocked; +contacts.blockedSlice#900802a1 count:int blocked:Vector users:Vector = contacts.Blocked; + +contacts.found#566000e results:Vector users:Vector = contacts.Found; + +contacts.suggested#5649dcc5 results:Vector users:Vector = contacts.Suggested; + +messages.dialogs#15ba6c40 dialogs:Vector messages:Vector chats:Vector users:Vector = messages.Dialogs; +messages.dialogsSlice#71e094f3 count:int dialogs:Vector messages:Vector chats:Vector users:Vector = messages.Dialogs; + +messages.messages#8c718e87 messages:Vector chats:Vector users:Vector = messages.Messages; +messages.messagesSlice#b446ae3 count:int messages:Vector chats:Vector users:Vector = messages.Messages; + +messages.messageEmpty#3f4e0648 = messages.Message; +messages.message#ff90c417 message:Message chats:Vector users:Vector = messages.Message; + +messages.statedMessages#969478bb messages:Vector chats:Vector users:Vector pts:int seq:int = messages.StatedMessages; + +messages.statedMessage#d07ae726 message:Message chats:Vector users:Vector pts:int seq:int = messages.StatedMessage; + +messages.sentMessage#d1f4d35c id:int date:int pts:int seq:int = messages.SentMessage; + +messages.chat#40e9002a chat:Chat users:Vector = messages.Chat; + +messages.chats#8150cbd8 chats:Vector users:Vector = messages.Chats; + +messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector users:Vector = messages.ChatFull; + +messages.affectedHistory#b7de36f2 pts:int seq:int offset:int = messages.AffectedHistory; + +inputMessagesFilterEmpty#57e2f66c = MessagesFilter; +inputMessagesFilterPhotos#9609a51c = MessagesFilter; +inputMessagesFilterVideo#9fc00e65 = MessagesFilter; +inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter; +inputMessagesFilterDocument#9eddf188 = MessagesFilter; +inputMessagesFilterAudio#cfc87522 = MessagesFilter; + +updateNewMessage#13abdb3 message:Message pts:int = Update; +updateMessageID#4e90bfd6 id:int random_id:long = Update; +updateReadMessages#c6649e31 messages:Vector pts:int = Update; +updateDeleteMessages#a92bfe26 messages:Vector pts:int = Update; +updateRestoreMessages#d15de04d messages:Vector pts:int = Update; +//updateUserTyping#6baa8508 user_id:int = Update; +//updateChatUserTyping#3c46cfe6 chat_id:int user_id:int = Update; +updateChatParticipants#7761198 participants:ChatParticipants = Update; +updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; +updateUserName#da22d9ad user_id:int first_name:string last_name:string = Update; +updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update; +updateContactRegistered#2575bbb9 user_id:int date:int = Update; +updateContactLink#51a48a9a user_id:int my_link:contacts.MyLink foreign_link:contacts.ForeignLink = Update; +updateActivation#6f690963 user_id:int = Update; +updateNewAuthorization#8f06529a auth_key_id:long date:int device:string location:string = Update; + +updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; + +updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference; +updates.difference#f49ca0 new_messages:Vector new_encrypted_messages:Vector other_updates:Vector chats:Vector users:Vector state:updates.State = updates.Difference; +updates.differenceSlice#a8fb1981 new_messages:Vector new_encrypted_messages:Vector other_updates:Vector chats:Vector users:Vector intermediate_state:updates.State = updates.Difference; + +updatesTooLong#e317af7e = Updates; +updateShortMessage#d3f45784 id:int from_id:int message:string pts:int date:int seq:int = Updates; +updateShortChatMessage#2b2fbd4e id:int from_id:int chat_id:int message:string pts:int date:int seq:int = Updates; +updateShort#78d4dec1 update:Update date:int = Updates; +updatesCombined#725b04c3 updates:Vector users:Vector chats:Vector date:int seq_start:int seq:int = Updates; +updates#74ae4240 updates:Vector users:Vector chats:Vector date:int seq:int = Updates; + +photos.photos#8dca6aa5 photos:Vector users:Vector = photos.Photos; +photos.photosSlice#15051f54 count:int photos:Vector users:Vector = photos.Photos; + +photos.photo#20212ca8 photo:Photo users:Vector = photos.Photo; + +upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; + +dcOption#2ec2a43c id:int hostname:string ip_address:string port:int = DcOption; + +config#2e54dd74 date:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int broadcast_size_max:int = Config; + +nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; + +help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate; +help.noAppUpdate#c45a6536 = help.AppUpdate; + +help.inviteText#18cb9f78 message:string = help.InviteText; + +messages.statedMessagesLinks#3e74f5c6 messages:Vector chats:Vector users:Vector links:Vector pts:int seq:int = messages.StatedMessages; + +messages.statedMessageLink#a9af2881 message:Message chats:Vector users:Vector links:Vector pts:int seq:int = messages.StatedMessage; + +messages.sentMessageLink#e9db4a3f id:int date:int pts:int seq:int links:Vector = messages.SentMessage; + +inputGeoChat#74d456fa chat_id:int access_hash:long = InputGeoChat; + +inputNotifyGeoChatPeer#4d8ddec8 peer:InputGeoChat = InputNotifyPeer; + +geoChat#75eaea5a id:int access_hash:long title:string address:string venue:string geo:GeoPoint photo:ChatPhoto participants_count:int date:int checked_in:Bool version:int = Chat; + +geoChatMessageEmpty#60311a9b chat_id:int id:int = GeoChatMessage; +geoChatMessage#4505f8e1 chat_id:int id:int from_id:int date:int message:string media:MessageMedia = GeoChatMessage; +geoChatMessageService#d34fa24e chat_id:int id:int from_id:int date:int action:MessageAction = GeoChatMessage; + +geochats.statedMessage#17b1578b message:GeoChatMessage chats:Vector users:Vector seq:int = geochats.StatedMessage; + +geochats.located#48feb267 results:Vector messages:Vector chats:Vector users:Vector = geochats.Located; + +geochats.messages#d1526db1 messages:Vector chats:Vector users:Vector = geochats.Messages; +geochats.messagesSlice#bc5863e8 count:int messages:Vector chats:Vector users:Vector = geochats.Messages; + +messageActionGeoChatCreate#6f038ebc title:string address:string = MessageAction; +messageActionGeoChatCheckin#c7d53de = MessageAction; + +updateNewGeoChatMessage#5a68e3f7 message:GeoChatMessage = Update; + +wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper; + +updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update; +updateEncryptedChatTyping#1710f156 chat_id:int = Update; +updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update; +updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update; + +encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat; +encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat; +encryptedChatRequested#c878527e id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat; +encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat; +encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat; + +inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat; + +encryptedFileEmpty#c21f497e = EncryptedFile; +encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile; + +inputEncryptedFileEmpty#1837c364 = InputEncryptedFile; +inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile; +inputEncryptedFile#5a17b5e5 id:long access_hash:long = InputEncryptedFile; + +inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation; + +encryptedMessage#ed18c118 random_id:long chat_id:int date:int bytes:bytes file:EncryptedFile = EncryptedMessage; +encryptedMessageService#23734b06 random_id:long chat_id:int date:int bytes:bytes = EncryptedMessage; + +messages.dhConfigNotModified#c0e24635 random:bytes = messages.DhConfig; +messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhConfig; + +messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage; +messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage; + +inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile; + +inputEncryptedFileBigUploaded#2dc173c8 id:long parts:int key_fingerprint:int = InputEncryptedFile; + +updateChatParticipantAdd#3a0eeb22 chat_id:int user_id:int inviter_id:int version:int = Update; +updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update; +updateDcOptions#8e5e9873 dc_options:Vector = Update; + +inputMediaUploadedAudio#4e498cab file:InputFile duration:int mime_type:string = InputMedia; +inputMediaAudio#89938781 id:InputAudio = InputMedia; +inputMediaUploadedDocument#34e794bd file:InputFile file_name:string mime_type:string = InputMedia; +inputMediaUploadedThumbDocument#3e46de5d file:InputFile thumb:InputFile file_name:string mime_type:string = InputMedia; +inputMediaDocument#d184e841 id:InputDocument = InputMedia; + +messageMediaDocument#2fda2204 document:Document = MessageMedia; +messageMediaAudio#c6b68300 audio:Audio = MessageMedia; + +inputAudioEmpty#d95adc84 = InputAudio; +inputAudio#77d440ff id:long access_hash:long = InputAudio; + +inputDocumentEmpty#72f0eaae = InputDocument; +inputDocument#18798952 id:long access_hash:long = InputDocument; + +inputAudioFileLocation#74dc404d id:long access_hash:long = InputFileLocation; +inputDocumentFileLocation#4e45abe9 id:long access_hash:long = InputFileLocation; + +audioEmpty#586988d8 id:long = Audio; +audio#c7ac6496 id:long access_hash:long user_id:int date:int duration:int mime_type:string size:int dc_id:int = Audio; + +documentEmpty#36f8c871 id:long = Document; +document#9efc6326 id:long access_hash:long user_id:int date:int file_name:string mime_type:string size:int thumb:PhotoSize dc_id:int = Document; + +help.support#17c6b5f6 phone_number:string user:User = help.Support; + +notifyPeer#9fd40bd8 peer:Peer = NotifyPeer; +notifyUsers#b4c83b4c = NotifyPeer; +notifyChats#c007cec3 = NotifyPeer; +notifyAll#74d07c60 = NotifyPeer; + +updateUserBlocked#80ece81a user_id:int blocked:Bool = Update; +updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update; + +auth.sentAppCode#e325edcf phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode; + +updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update; +updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update; + + +message#567699b3 flags:int id:int from_id:int to_id:Peer date:int message:string media:MessageMedia = Message; +messageForwarded#a367e716 flags:int id:int fwd_from_id:int fwd_date:int from_id:int to_id:Peer date:int message:string media:MessageMedia = Message; +messageService#1d86f70e flags:int id:int from_id:int to_id:Peer date:int action:MessageAction = Message; + + +sendMessageTypingAction#16bf744e = SendMessageAction; +sendMessageCancelAction#fd5ec8f5 = SendMessageAction; +sendMessageRecordVideoAction#a187d66f = SendMessageAction; +sendMessageUploadVideoAction#92042ff7 = SendMessageAction; +sendMessageRecordAudioAction#d52f73f7 = SendMessageAction; +sendMessageUploadAudioAction#e6ac8a6f = SendMessageAction; +sendMessageUploadPhotoAction#990a3c1a = SendMessageAction; +sendMessageUploadDocumentAction#8faee98e = SendMessageAction; +sendMessageGeoLocationAction#176f8ba1 = SendMessageAction; +sendMessageChooseContactAction#628cbc6f = SendMessageAction; + +---functions--- + +invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; + +invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector query:!X = X; + +auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone; +auth.sendCode#768d5f4d phone_number:string sms_type:int api_id:int api_hash:string lang_code:string = auth.SentCode; +auth.sendCall#3c51564 phone_number:string phone_code_hash:string = Bool; +auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; +auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; +auth.logOut#5717da40 = Bool; +auth.resetAuthorizations#9fab0d1a = Bool; +auth.sendInvites#771c1d97 phone_numbers:Vector message:string = Bool; +auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; +auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; +auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; + +account.registerDevice#446c712c token_type:int token:string device_model:string system_version:string app_version:string app_sandbox:Bool lang_code:string = Bool; +account.unregisterDevice#65c55b40 token_type:int token:string = Bool; +account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; +account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; +account.resetNotifySettings#db7e1747 = Bool; +account.updateProfile#f0888d68 first_name:string last_name:string = User; +account.updateStatus#6628562c offline:Bool = Bool; +account.getWallPapers#c04cfac2 = Vector; + +users.getUsers#d91a548 id:Vector = Vector; +users.getFullUser#ca30a5b1 id:InputUser = UserFull; + +contacts.getStatuses#c4a353ee = Vector; +contacts.getContacts#22c6aa08 hash:string = contacts.Contacts; +contacts.importContacts#da30b32d contacts:Vector replace:Bool = contacts.ImportedContacts; +contacts.search#11f812d8 q:string limit:int = contacts.Found; +contacts.getSuggested#cd773428 limit:int = contacts.Suggested; +contacts.deleteContact#8e953744 id:InputUser = contacts.Link; +contacts.deleteContacts#59ab389e id:Vector = Bool; +contacts.block#332b49fc id:InputUser = Bool; +contacts.unblock#e54100bd id:InputUser = Bool; +contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked; +contacts.exportCard#84e53737 = Vector; +contacts.importCard#4fe196fe export_card:Vector = User; + +messages.getMessages#4222fa74 id:Vector = messages.Messages; +messages.getDialogs#eccf1df6 offset:int max_id:int limit:int = messages.Dialogs; +messages.getHistory#92a1df2f peer:InputPeer offset:int max_id:int limit:int = messages.Messages; +messages.search#7e9f2ab peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages; +messages.readHistory#eed884c6 peer:InputPeer max_id:int offset:int read_contents:Bool = messages.AffectedHistory; +messages.deleteHistory#f4f8fb61 peer:InputPeer offset:int = messages.AffectedHistory; +messages.deleteMessages#14f2dd0a id:Vector = Vector; +messages.restoreMessages#395f9d7e id:Vector = Vector; +messages.receivedMessages#28abcb68 max_id:int = Vector; +messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool; +messages.sendMessage#4cde0aab peer:InputPeer message:string random_id:long = messages.SentMessage; +messages.sendMedia#a3c85d76 peer:InputPeer media:InputMedia random_id:long = messages.StatedMessage; +messages.forwardMessages#514cd10f peer:InputPeer id:Vector = messages.StatedMessages; +messages.getChats#3c6aa187 id:Vector = messages.Chats; +messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; +messages.editChatTitle#b4bc68b5 chat_id:int title:string = messages.StatedMessage; +messages.editChatPhoto#d881821d chat_id:int photo:InputChatPhoto = messages.StatedMessage; +messages.addChatUser#2ee9ee9e chat_id:int user_id:InputUser fwd_limit:int = messages.StatedMessage; +messages.deleteChatUser#c3c5cd23 chat_id:int user_id:InputUser = messages.StatedMessage; +messages.createChat#419d9aee users:Vector title:string = messages.StatedMessage; + +updates.getState#edd4882a = updates.State; +updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference; + +photos.updateProfilePhoto#eef579a0 id:InputPhoto crop:InputPhotoCrop = UserProfilePhoto; +photos.uploadProfilePhoto#d50f9c88 file:InputFile caption:string geo_point:InputGeoPoint crop:InputPhotoCrop = photos.Photo; + +upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool; +upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File; + +help.getConfig#c4f9186b = Config; +help.getNearestDc#1fb33026 = NearestDc; +help.getAppUpdate#c812ac7e device_model:string system_version:string app_version:string lang_code:string = help.AppUpdate; +help.saveAppLog#6f02f748 events:Vector = Bool; +help.getInviteText#a4a95186 lang_code:string = help.InviteText; + +photos.getUserPhotos#b7ee553c user_id:InputUser offset:int max_id:int limit:int = photos.Photos; + +messages.forwardMessage#3f3f4f2 peer:InputPeer id:int random_id:long = messages.StatedMessage; +messages.sendBroadcast#41bb0972 contacts:Vector message:string media:InputMedia = messages.StatedMessages; + +geochats.getLocated#7f192d8f geo_point:InputGeoPoint radius:int limit:int = geochats.Located; +geochats.getRecents#e1427e6f offset:int limit:int = geochats.Messages; +geochats.checkin#55b3e8fb peer:InputGeoChat = geochats.StatedMessage; +geochats.getFullChat#6722dd6f peer:InputGeoChat = messages.ChatFull; +geochats.editChatTitle#4c8e2273 peer:InputGeoChat title:string address:string = geochats.StatedMessage; +geochats.editChatPhoto#35d81a95 peer:InputGeoChat photo:InputChatPhoto = geochats.StatedMessage; +geochats.search#cfcdc44d peer:InputGeoChat q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = geochats.Messages; +geochats.getHistory#b53f7a68 peer:InputGeoChat offset:int max_id:int limit:int = geochats.Messages; +geochats.setTyping#8b8a729 peer:InputGeoChat typing:Bool = Bool; +geochats.sendMessage#61b0044 peer:InputGeoChat message:string random_id:long = geochats.StatedMessage; +geochats.sendMedia#b8f0deff peer:InputGeoChat media:InputMedia random_id:long = geochats.StatedMessage; +geochats.createGeoChat#e092e16 title:string geo_point:InputGeoPoint address:string venue:string = geochats.StatedMessage; + +messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; +messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; +messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; +messages.discardEncryption#edd923c5 chat_id:int = Bool; +messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool; +messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool; +messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; +messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage; +messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; +messages.receivedQueue#55a5bb66 max_qts:int = Vector; + +upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; + +initConnection#69796de9 {X:Type} api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X; + +help.getSupport#9cdf08cd = help.Support; + +auth.sendSms#da9f3e8 phone_number:string phone_code_hash:string = Bool; + +messages.readMessageContents id:Vector = Vector; + + +invokeWithLayer17#50858a19 {X:Type} query:!X = X; + diff --git a/server.pub b/server.pub new file mode 100644 index 0000000..5e38bb0 --- /dev/null +++ b/server.pub @@ -0,0 +1,8 @@ +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 +lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS +an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw +Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ +8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n +Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB +-----END RSA PUBLIC KEY----- diff --git a/structures.c b/structures.c index 9240018..148ad3e 100644 --- a/structures.c +++ b/structures.c @@ -108,6 +108,34 @@ char *tgls_default_create_print_name (tgl_peer_id_t id, const char *a1, const ch return tstrdup (s); } +enum tgl_typing_status tglf_fetch_typing (void) { + switch (fetch_int ()) { + 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; + } +} + /* {{{ Fetch */ int tglf_fetch_file_location (struct tgl_file_location *loc) { @@ -512,8 +540,8 @@ void tglf_fetch_photo_size (struct tgl_photo_size *S) { void tglf_fetch_geo (struct tgl_geo *G) { unsigned x = fetch_int (); if (x == CODE_geo_point) { - G->longitude = fetch_double (); G->latitude = fetch_double (); + G->longitude = fetch_double (); } else { assert (x == CODE_geo_point_empty); G->longitude = 0; @@ -923,8 +951,8 @@ void tglf_fetch_message_media_encrypted (struct tgl_message_media *M) { */ case CODE_decrypted_message_media_geo_point: M->type = tgl_message_media_geo; - M->geo.longitude = fetch_double (); M->geo.latitude = fetch_double (); + M->geo.longitude = fetch_double (); break; case CODE_decrypted_message_media_contact: M->type = tgl_message_media_contact; @@ -990,6 +1018,10 @@ void tglf_fetch_message_action_encrypted (struct tgl_message_action *M) { 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 (); + break; default: vlogprintf (E_ERROR, "x = 0x%08x\n", x); assert (0); @@ -1009,6 +1041,10 @@ tgl_peer_id_t tglf_fetch_peer_id (void) { void tglf_fetch_message (struct tgl_message *M) { unsigned x = fetch_int (); assert (x == CODE_message_empty || x == CODE_message || x == CODE_message_forwarded || x == CODE_message_service); + int flags = 0; + if (x != CODE_message_empty) { + flags = fetch_int (); + } int id = fetch_int (); assert (M->id == id); if (x == CODE_message_empty) { @@ -1024,11 +1060,12 @@ void tglf_fetch_message (struct tgl_message *M) { int from_id = fetch_int (); tgl_peer_id_t to_id = tglf_fetch_peer_id (); - fetch_bool (); // out. + //fetch_bool (); // out. - int unread = fetch_bool (); + //int unread = fetch_bool (); int date = fetch_int (); + int unread = (flags & 1) != 0; int new = !(M->flags & FLAG_CREATED); if (x == CODE_message_service) { @@ -1190,12 +1227,30 @@ void tglf_fetch_encrypted_message (struct tgl_message *M) { assert (layer >= 0); x = fetch_int (); } - assert (x == CODE_decrypted_message || x == CODE_decrypted_message_service); + assert (x == CODE_decrypted_message || x == CODE_decrypted_message_service || x == CODE_decrypted_message_l16 || x == CODE_decrypted_message_service_l16); //assert (id == fetch_long ()); + if (x == CODE_decrypted_message || x == CODE_decrypted_message_service) { + int out_seq_no = fetch_int (); + int in_seq_no = fetch_int (); + 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 + 1); + } + if (in_seq_no / 2 > P->encr_chat.in_seq_no + 1) { + 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 + 1); + } + //vlogprintf (E_WARNING, "in = %d, out = %d\n", in_seq_no, out_seq_no); + assert (out_seq_no / 2 <= P->encr_chat.out_seq_no); + P->encr_chat.in_seq_no = in_seq_no / 2; + if (x == CODE_decrypted_message) { + fetch_int (); // ttl + } + } else { + P->encr_chat.in_seq_no ++; + } fetch_long (); ll = prefetch_strlen (); fetch_str (ll); // random_bytes - if (x == CODE_decrypted_message) { + if (x == CODE_decrypted_message || x == CODE_decrypted_message_l16) { l = prefetch_strlen (); s = fetch_str (l); start = in_ptr; @@ -1216,9 +1271,9 @@ void tglf_fetch_encrypted_message (struct tgl_message *M) { if (ok) { int *start_file = in_ptr; assert (skip_type_any (TYPE_TO_PARAM (encrypted_file)) >= 0); - if (x == CODE_decrypted_message) { + if (x == CODE_decrypted_message || x == CODE_decrypted_message_l16) { bl_do_create_message_media_encr (id, P->encr_chat.user_id, TGL_PEER_ENCR_CHAT, to_id, date, l, s, start, end - start, start_file, in_ptr - start_file); - } else if (x == CODE_decrypted_message_service) { + } else if (x == CODE_decrypted_message_service || x == CODE_decrypted_message_service_l16) { bl_do_create_message_service_encr (id, P->encr_chat.user_id, TGL_PEER_ENCR_CHAT, to_id, date, start, end - start); } } else { @@ -1226,7 +1281,7 @@ void tglf_fetch_encrypted_message (struct tgl_message *M) { M->media.type = CODE_message_media_empty; } } else { - if (ok && x == CODE_decrypted_message_service) { + if (ok && (x == CODE_decrypted_message_service || x == CODE_decrypted_message_service_l16)) { bl_do_create_message_service_encr (id, P->encr_chat.user_id, TGL_PEER_ENCR_CHAT, to_id, date, start, end - start); } } @@ -1310,12 +1365,12 @@ struct tgl_user *tglf_fetch_alloc_user_full (void) { } struct tgl_message *tglf_fetch_alloc_message (void) { - int data[2]; - prefetch_data (data, 8); - struct tgl_message *M = tgl_message_get (data[1]); + int data[3]; + prefetch_data (data, 12); + struct tgl_message *M = tgl_message_get (data[0] != (int)CODE_message_empty ? data[2] : data[1]); if (!M) { - M = tglm_message_alloc (data[1]); + M = tglm_message_alloc (data[0] != (int)CODE_message_empty ? data[2] : data[1]); } tglf_fetch_message (M); return M; diff --git a/structures.h b/structures.h index d9db912..92e2f60 100644 --- a/structures.h +++ b/structures.h @@ -51,5 +51,6 @@ void tglp_peer_delete_name (tgl_peer_t *P); void tglp_insert_encrypted_chat (tgl_peer_t *P); void tglp_insert_user (tgl_peer_t *P); void tglp_insert_chat (tgl_peer_t *P); +enum tgl_typing_status tglf_fetch_typing (void); #endif diff --git a/tgl-layout.h b/tgl-layout.h index 0f08bc6..ed470de 100644 --- a/tgl-layout.h +++ b/tgl-layout.h @@ -120,7 +120,22 @@ enum tgl_message_action_type { tgl_message_action_delete_messages, tgl_message_action_screenshot_messages, tgl_message_action_flush_history, - tgl_message_action_notify_layer + tgl_message_action_notify_layer, + tgl_message_action_typing +}; + +enum tgl_typing_status { + tgl_typing_none, + tgl_typing_typing, + tgl_typing_cancel, + tgl_typing_record_video, + tgl_typing_upload_video, + tgl_typing_record_audio, + tgl_typing_upload_audio, + tgl_typing_upload_photo, + tgl_typing_upload_document, + tgl_typing_geo, + tgl_typing_choose_contact }; struct tgl_file_location { @@ -288,6 +303,8 @@ struct tgl_secret_chat { int date; int ttl; int layer; + int in_seq_no; + int out_seq_no; long long access_hash; unsigned char *g_key; unsigned char *nonce; @@ -367,6 +384,7 @@ struct tgl_message_action { int read_cnt; int delete_cnt; int screenshot_cnt; + enum tgl_typing_status typing; }; }; diff --git a/tgl.h b/tgl.h index 529dccf..617b2f4 100644 --- a/tgl.h +++ b/tgl.h @@ -24,16 +24,22 @@ #include #define TGL_MAX_DC_NUM 100 -#define TG_SERVER "149.154.167.50" -#define TG_SERVER_TEST "149.154.167.40" -#define TG_SERVER_DC 2 -#define TG_SERVER_TEST_DC 2 +#define TG_SERVER_1 "173.240.5.1" +#define TG_SERVER_2 "149.154.167.51" +#define TG_SERVER_3 "174.140.142.6" +#define TG_SERVER_4 "149.154.167.91" +#define TG_SERVER_5 "149.154.171.5" + + +#define TG_SERVER_TEST_1 "173.240.5.253" +#define TG_SERVER_TEST_2 "149.154.167.40" +#define TG_SERVER_TEST_3 "174.140.142.5" // JUST RANDOM STRING -#define TGL_BUILD "2014" -#define TGL_VERSION "1.0.5" +#define TGL_BUILD "2234" +#define TGL_VERSION "1.0.6" -#define TGL_ENCRYPTED_LAYER 16 +#define TGL_ENCRYPTED_LAYER 17 struct connection; struct mtproto_methods; @@ -70,8 +76,8 @@ struct tgl_update_callback { void (*new_msg)(struct tgl_message *M); void (*marked_read)(int num, struct tgl_message *list[]); void (*logprintf)(const char *format, ...) __attribute__ ((format (printf, 1, 2))); - void (*type_notification)(struct tgl_user *U); - void (*type_in_chat_notification)(struct tgl_user *U, struct tgl_chat *C); + void (*type_notification)(struct tgl_user *U, enum tgl_typing_status status); + void (*type_in_chat_notification)(struct tgl_user *U, struct tgl_chat *C, enum tgl_typing_status status); void (*type_in_secret_chat_notification)(struct tgl_secret_chat *E); void (*status_notification)(struct tgl_user *U); void (*user_registered)(struct tgl_user *U); @@ -289,6 +295,7 @@ void tgl_do_send_contact (tgl_peer_id_t id, const char *phone, int phone_len, co void tgl_do_forward_media (tgl_peer_id_t id, int n, void (*callback)(void *callback_extra, int success, struct tgl_message *M), void *callback_extra); void tgl_do_del_contact (tgl_peer_id_t id, void (*callback)(void *callback_extra, int success), void *callback_extra); void tgl_do_set_encr_chat_ttl (struct tgl_secret_chat *E, int ttl, void (*callback)(void *callback_extra, int success, struct tgl_message *M), void *callback_extra); +void tgl_do_send_location(tgl_peer_id_t id, double latitude, double longitude, void (*callback)(void *callback_extra, int success, struct tgl_message *M), void *callback_extra); void tgl_do_visualize_key (tgl_peer_id_t id, unsigned char buf[16]); diff --git a/updates.c b/updates.c index 128cf52..32e95ce 100644 --- a/updates.c +++ b/updates.c @@ -99,24 +99,28 @@ void tglu_work_update (struct connection *c, long long msg_id) { break; case CODE_update_user_typing: { + //vlogprintf (E_ERROR, "user typing\n"); tgl_peer_id_t id = TGL_MK_USER (fetch_int ()); tgl_peer_t *U = tgl_peer_get (id); + enum tgl_typing_status status = tglf_fetch_typing (); if (tgl_state.callback.type_notification && U) { - tgl_state.callback.type_notification ((void *)U); + tgl_state.callback.type_notification ((void *)U, status); } } break; case CODE_update_chat_user_typing: { + //vlogprintf (E_ERROR, "chat typing\n"); tgl_peer_id_t chat_id = TGL_MK_CHAT (fetch_int ()); tgl_peer_id_t id = TGL_MK_USER (fetch_int ()); tgl_peer_t *C = tgl_peer_get (chat_id); tgl_peer_t *U = tgl_peer_get (id); + enum tgl_typing_status status = tglf_fetch_typing (); if (U && C) { if (tgl_state.callback.type_in_chat_notification) { - tgl_state.callback.type_in_chat_notification ((void *)U, (void *)C); + tgl_state.callback.type_in_chat_notification ((void *)U, (void *)C, status); } } }