diff --git a/interface.c b/interface.c index 95f1558..536d3bc 100644 --- a/interface.c +++ b/interface.c @@ -48,9 +48,8 @@ long long cur_downloading_bytes; long long cur_downloaded_bytes; char *line_ptr; -extern int user_num; -extern int chat_num; extern peer_t *Peers[]; +extern int peer_num; int is_same_word (const char *s, size_t l, const char *word) { return s && word && strlen (word) == l && !memcmp (s, word, l); @@ -106,10 +105,10 @@ peer_id_t next_token_user (void) { } int index = 0; - while (index < user_num + chat_num && (!is_same_word (s, l, Peers[index]->print_name) || get_peer_type (Peers[index]->id) != PEER_USER)) { + while (index < peer_num && (!is_same_word (s, l, Peers[index]->print_name) || get_peer_type (Peers[index]->id) != PEER_USER)) { index ++; } - if (index < user_num + chat_num) { + if (index < peer_num) { return Peers[index]->id; } else { return PEER_NOT_FOUND; @@ -130,10 +129,10 @@ peer_id_t next_token_chat (void) { } int index = 0; - while (index < user_num + chat_num && (!is_same_word (s, l, Peers[index]->print_name) || get_peer_type (Peers[index]->id) != PEER_CHAT)) { + while (index < peer_num && (!is_same_word (s, l, Peers[index]->print_name) || get_peer_type (Peers[index]->id) != PEER_CHAT)) { index ++; } - if (index < user_num + chat_num) { + if (index < peer_num) { return Peers[index]->id; } else { return PEER_NOT_FOUND; @@ -161,10 +160,10 @@ peer_id_t next_token_peer (void) { } int index = 0; - while (index < user_num + chat_num && (!is_same_word (s, l, Peers[index]->print_name))) { + while (index < peer_num && (!is_same_word (s, l, Peers[index]->print_name))) { index ++; } - if (index < user_num + chat_num) { + if (index < peer_num) { return Peers[index]->id; } else { return PEER_NOT_FOUND; @@ -232,6 +231,7 @@ char *commands[] = { "rename_contact", "show_license", "search", + "mark_read", 0 }; int commands_flags[] = { @@ -258,6 +258,7 @@ int commands_flags[] = { 071, 07, 072, + 072, }; int get_complete_mode (void) { @@ -297,10 +298,10 @@ int get_complete_mode (void) { int complete_user_list (int index, const char *text, int len, char **R) { index ++; - while (index < user_num + chat_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len) || get_peer_type (Peers[index]->id) != PEER_USER)) { + while (index < peer_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len) || get_peer_type (Peers[index]->id) != PEER_USER)) { index ++; } - if (index < user_num + chat_num) { + if (index < peer_num) { *R = strdup (Peers[index]->print_name); return index; } else { @@ -310,10 +311,10 @@ int complete_user_list (int index, const char *text, int len, char **R) { int complete_chat_list (int index, const char *text, int len, char **R) { index ++; - while (index < user_num + chat_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len) || get_peer_type (Peers[index]->id) != PEER_CHAT)) { + while (index < peer_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len) || get_peer_type (Peers[index]->id) != PEER_CHAT)) { index ++; } - if (index < user_num + chat_num) { + if (index < peer_num) { *R = strdup (Peers[index]->print_name); return index; } else { @@ -323,10 +324,10 @@ int complete_chat_list (int index, const char *text, int len, char **R) { int complete_user_chat_list (int index, const char *text, int len, char **R) { index ++; - while (index < user_num + chat_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len))) { + while (index < peer_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len))) { index ++; } - if (index < user_num + chat_num) { + if (index < peer_num) { *R = strdup (Peers[index]->print_name); return index; } else { @@ -663,6 +664,8 @@ void interpreter (char *line UU) { "load_photo/load_video/load_video_thumb - loads photo/video to download dir\n" "view_photo/view_video/view_video_thumb - loads photo/video to download dir and starts system default viewer\n" "show_license - prints contents of GPLv2\n" + "search pattern - searches pattern in messages with peer\n" + "mark_read - mark read all received messages with peer\n" ); pop_color (); rl_on_new_line (); @@ -685,6 +688,9 @@ void interpreter (char *line UU) { RET; } do_msg_search (id, from, to, limit, s); + } else if (IS_WORD ("mark_read")) { + GET_PEER; + do_mark_read (id); } #undef IS_WORD #undef RET @@ -796,30 +802,6 @@ void logprintf (const char *format, ...) { } } -const char *message_media_type_str (struct message_media *M) { - static char buf[1000]; - switch (M->type) { - case CODE_message_media_empty: - return ""; - case CODE_message_media_photo: - return "[photo]"; - case CODE_message_media_video: - return "[video]"; - case CODE_message_media_geo: - sprintf (buf, "[geo] https://maps.google.com/maps?ll=%.6lf,%.6lf", M->geo.latitude, M->geo.longitude); - return buf; - case CODE_message_media_contact: - snprintf (buf, 999, "[contact] " COLOR_RED "%s %s" COLOR_NORMAL " %s", M->first_name, M->last_name, M->phone); - return buf; - case CODE_message_media_unsupported: - return "[unsupported]"; - default: - assert (0); - return ""; - - } -} - int color_stack_pos; const char *color_stack[10]; @@ -853,6 +835,12 @@ void print_media (struct message_media *M) { case CODE_message_media_video: printf ("[video]"); return; + case CODE_decrypted_message_media_photo: + printf ("[photo]"); + return; + case CODE_decrypted_message_media_video: + printf ("[video]"); + return; case CODE_message_media_geo: printf ("[geo] https://maps.google.com/?q=%.6lf,%.6lf", M->geo.latitude, M->geo.longitude); return; @@ -1009,6 +997,9 @@ peer_id_t last_from_id; peer_id_t last_to_id; void print_message (struct message *M) { + if (M->flags & (FLAG_EMPTY | FLAG_DELETED)) { + return; + } if (M->service) { print_service_message (M); return; @@ -1050,7 +1041,42 @@ void print_message (struct message *M) { printf (" »»» "); } } + } else if (get_peer_type (M->to_id) == PEER_ENCR_CHAT) { + peer_t *P = user_chat_get (M->to_id); + assert (P); + if (M->out) { + push_color (COLOR_GREEN); + if (msg_num_mode) { + printf ("%lld ", M->id); + } + print_date (M->date); + printf (" "); + push_color (COLOR_CYAN); + printf (" %s", P->print_name); + pop_color (); + if (M->unread) { + printf (" <<< "); + } else { + printf (" ««« "); + } + } else { + push_color (COLOR_BLUE); + if (msg_num_mode) { + printf ("%lld ", M->id); + } + print_date (M->date); + push_color (COLOR_CYAN); + printf (" %s", P->print_name); + pop_color (); + if (M->unread) { + printf (" >>> "); + } else { + printf (" »»» "); + } + } + } else { + assert (get_peer_type (M->to_id) == PEER_CHAT); push_color (COLOR_MAGENTA); if (msg_num_mode) { printf ("%lld ", M->id); diff --git a/interface.h b/interface.h index b529762..1a4bd32 100644 --- a/interface.h +++ b/interface.h @@ -28,7 +28,7 @@ #define COLOR_YELLOW "\033[33;1m" #define COLOR_BLUE "\033[34;1m" #define COLOR_MAGENTA "\033[35;1m" - +#define COLOR_CYAN "\033[36;1m" char *get_default_prompt (void); char *complete_none (const char *text, int state); diff --git a/loop.c b/loop.c index ea9c065..0e7add0 100644 --- a/loop.c +++ b/loop.c @@ -39,6 +39,7 @@ #include "mtproto-common.h" #include "queries.h" #include "telegram.h" +#include "loop.h" extern char *default_username; extern char *auth_token; @@ -59,6 +60,7 @@ void net_loop (int flags, int (*is_end)(void)) { cc ++; } + write_state_file (); int x = connections_make_poll_array (fds + cc, 101 - cc) + cc; double timer = next_timer_in (); if (timer > 1000) { timer = 1000; } @@ -116,6 +118,8 @@ struct dc *DC_working; int dc_working_num; int auth_state; char *get_auth_key_filename (void); +char *get_state_filename (void); +char *get_secret_chat_filename (void); int zero[512]; @@ -231,11 +235,157 @@ void read_auth_file (void) { } } +int pts, qts, seq, last_date; + +void read_state_file (void) { + int state_file_fd = open (get_state_filename (), O_CREAT | O_RDWR, 0600); + if (state_file_fd < 0) { + return; + } + int version, magic; + if (read (state_file_fd, &magic, 4) < 4) { close (state_file_fd); return; } + if (magic != (int)STATE_FILE_MAGIC) { close (state_file_fd); return; } + if (read (state_file_fd, &version, 4) < 4) { close (state_file_fd); return; } + assert (version >= 0); + int x[4]; + if (read (state_file_fd, x, 16) < 16) { + close (state_file_fd); + return; + } + pts = x[0]; + qts = x[1]; + seq = x[2]; + last_date = x[3]; + close (state_file_fd); +} + +void write_state_file (void) { + static int wseq; + static int wpts; + static int wqts; + static int wdate; + if (wseq >= seq && wpts >= pts && wqts >= qts && wdate >= last_date) { return; } + int state_file_fd = open (get_state_filename (), O_CREAT | O_RDWR, 0600); + if (state_file_fd < 0) { + return; + } + int x[6]; + x[0] = STATE_FILE_MAGIC; + x[1] = 0; + x[2] = pts; + x[3] = qts; + x[4] = seq; + x[5] = last_date; + assert (write (state_file_fd, x, 24) == 24); + close (state_file_fd); + wseq = seq; wpts = pts; wqts = qts; wdate = last_date; +} + +extern peer_t *Peers[]; +extern int peer_num; + +void read_secret_chat_file (void) { + int fd = open (get_secret_chat_filename (), O_CREAT | O_RDWR, 0600); + if (fd < 0) { + return; + } + int x[2]; + if (read (fd, x, 8) < 8) { + close (fd); return; + } + if (x[0] != (int)SECRET_CHAT_FILE_MAGIC) { close (fd); return; } + int version = x[1]; + assert (version >= 0); + int cc; + assert (read (fd, &cc, 4) == 4); + int i; + for (i = 0; i < cc; i++) { + peer_t *P = malloc (sizeof (*P)); + memset (P, 0, sizeof (*P)); + struct secret_chat *E = &P->encr_chat; + int t; + assert (read (fd, &t, 4) == 4); + P->id = MK_ENCR_CHAT (t); + assert (read (fd, &P->flags, 4) == 4); + assert (read (fd, &t, 4) == 4); + assert (t > 0); + P->print_name = malloc (t + 1); + assert (read (fd, P->print_name, t) == t); + P->print_name[t] = 0; + + assert (read (fd, &E->state, 4) == 4); + assert (read (fd, &E->user_id, 4) == 4); + assert (read (fd, &E->admin_id, 4) == 4); + assert (read (fd, &E->ttl, 4) == 4); + assert (read (fd, &E->access_hash, 8) == 8); + + if (E->state != sc_waiting) { + E->g_key = malloc (256); + assert (read (fd, E->g_key, 256) == 256); + } + E->nonce = malloc (256); + assert (read (fd, E->nonce, 256) == 256); + assert (read (fd, E->key, 256) == 256); + assert (read (fd, &E->key_fingerprint, 8) == 8); + insert_encrypted_chat (P); + } + close (fd); +} + +void write_secret_chat_file (void) { + int fd = open (get_secret_chat_filename (), O_CREAT | O_RDWR, 0600); + if (fd < 0) { + return; + } + int x[2]; + x[0] = SECRET_CHAT_FILE_MAGIC; + x[1] = 0; + assert (write (fd, x, 8) == 8); + int i; + int cc = 0; + for (i = 0; i < peer_num; i++) if (get_peer_type (Peers[i]->id) == PEER_ENCR_CHAT) { + if (Peers[i]->encr_chat.state != sc_none && Peers[i]->encr_chat.state != sc_deleted) { + cc ++; + } + } + assert (write (fd, &cc, 4) == 4); + for (i = 0; i < peer_num; i++) if (get_peer_type (Peers[i]->id) == PEER_ENCR_CHAT) { + if (Peers[i]->encr_chat.state != sc_none && Peers[i]->encr_chat.state != sc_deleted) { + int t = get_peer_id (Peers[i]->id); + assert (write (fd, &t, 4) == 4); + t = Peers[i]->flags; + assert (write (fd, &t, 4) == 4); + t = strlen (Peers[i]->print_name); + assert (write (fd, &t, 4) == 4); + assert (write (fd, Peers[i]->print_name, t) == t); + + assert (write (fd, &Peers[i]->encr_chat.state, 4) == 4); + + assert (write (fd, &Peers[i]->encr_chat.user_id, 4) == 4); + assert (write (fd, &Peers[i]->encr_chat.admin_id, 4) == 4); + assert (write (fd, &Peers[i]->encr_chat.ttl, 4) == 4); + assert (write (fd, &Peers[i]->encr_chat.access_hash, 8) == 8); + if (Peers[i]->encr_chat.state != sc_waiting) { + assert (write (fd, Peers[i]->encr_chat.g_key, 256) == 256); + } + assert (write (fd, Peers[i]->encr_chat.nonce, 256) == 256); + assert (write (fd, Peers[i]->encr_chat.key, 256) == 256); + assert (write (fd, &Peers[i]->encr_chat.key_fingerprint, 8) == 8); + } + } + close (fd); +} + extern int max_chat_size; int mcs (void) { return max_chat_size; } +extern int difference_got; +int dgot (void) { + return !difference_got; +} + int readline_active; int new_dc_num; int loop (void) { @@ -368,7 +518,11 @@ int loop (void) { rl_attempted_completion_function = (CPPFunction *) complete_text; rl_completion_entry_function = complete_none; - do_get_dialog_list_ex (); + read_state_file (); + read_secret_chat_file (); + do_get_difference (); + net_loop (0, dgot); + do_get_dialog_list (); return main_loop (); } diff --git a/loop.h b/loop.h index 2b14f33..6f7c7e4 100644 --- a/loop.h +++ b/loop.h @@ -21,4 +21,6 @@ int loop (void); void net_loop (int flags, int (*end)(void)); void write_auth_file (void); +void write_state_file (void); +void write_secret_chat_file (void); #endif diff --git a/main.c b/main.c index e49744f..0bd5580 100644 --- a/main.c +++ b/main.c @@ -41,6 +41,8 @@ #define CONFIG_DIRECTORY ".telegram/" #define CONFIG_FILE CONFIG_DIRECTORY "config" #define AUTH_KEY_FILE CONFIG_DIRECTORY "auth" +#define STATE_FILE CONFIG_DIRECTORY "state" +#define SECRET_CHAT_FILE CONFIG_DIRECTORY "secret" #define DOWNLOADS_DIRECTORY "downloads/" #define CONFIG_DIRECTORY_MODE 0700 @@ -130,6 +132,24 @@ char *get_auth_key_filename (void) { return auth_key_filename; } +char *get_state_filename (void) { + char *state_filename; + int length = strlen (get_home_directory ()) + strlen (STATE_FILE) + 2; + + state_filename = (char *) calloc (length, sizeof (char)); + sprintf (state_filename, "%s/" STATE_FILE, get_home_directory ()); + return state_filename; +} + +char *get_secret_chat_filename (void) { + char *secret_chat_filename; + int length = strlen (get_home_directory ()) + strlen (SECRET_CHAT_FILE) + 2; + + secret_chat_filename = (char *) calloc (length, sizeof (char)); + sprintf (secret_chat_filename, "%s/" SECRET_CHAT_FILE, get_home_directory ()); + return secret_chat_filename; +} + char *get_downloads_directory (void) { char *downloads_directory; diff --git a/mtproto-client.c b/mtproto-client.c index 7152a40..467d4a8 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -637,14 +637,18 @@ int unread_messages; int our_id; int pts; int qts; +int last_date; +int seq; void fetch_pts (void) { int p = fetch_int (); + if (p <= pts) { return; } if (p != pts + 1) { if (pts) { - logprintf ("Hole in pts p = %d, pts = %d\n", p, pts); + //logprintf ("Hole in pts p = %d, pts = %d\n", p, pts); // get difference should be here + pts = p; } else { pts = p; } @@ -655,10 +659,12 @@ void fetch_pts (void) { void fetch_qts (void) { int p = fetch_int (); + if (p <= qts) { return; } if (p != qts + 1) { if (qts) { - logprintf ("Hole in qts\n"); + //logprintf ("Hole in qts\n"); // get difference should be here + qts = p; } else { qts = p; } @@ -667,6 +673,24 @@ void fetch_qts (void) { } } +void fetch_date (void) { + int p = fetch_int (); + if (p > last_date) { + last_date = p; + } +} + +void fetch_seq (void) { + int x = fetch_int (); + if (x > seq + 1) { + logprintf ("Hole in seq: seq = %d, x = %d\n", seq, x); + //do_get_difference (); + seq = x; + } else if (x >= seq + 1) { + seq = x; + } +} + void work_update (struct connection *c UU, long long msg_id UU) { unsigned op = fetch_int (); switch (op) { @@ -993,9 +1017,11 @@ void work_update (struct connection *c UU, long long msg_id UU) { printf (" is now in deleted state\n"); break; } - /*if (E->state == state_requested) { + pop_color (); + print_end (); + if (E->state == sc_request) { do_accept_encr_chat_request (E); - }*/ + } fetch_int (); // date } break; @@ -1041,7 +1067,7 @@ void work_update (struct connection *c UU, long long msg_id UU) { void work_update_short (struct connection *c, long long msg_id) { assert (fetch_int () == CODE_update_short); work_update (c, msg_id); - fetch_int (); // date + fetch_date (); } void work_updates (struct connection *c, long long msg_id) { @@ -1062,9 +1088,8 @@ void work_updates (struct connection *c, long long msg_id) { for (i = 0; i < n; i++) { fetch_alloc_chat (); } - fetch_int (); // date - fetch_int (); // seq - + fetch_date (); // date + fetch_seq (); // seq } void work_update_short_message (struct connection *c UU, long long msg_id UU) { @@ -1073,6 +1098,9 @@ void work_update_short_message (struct connection *c UU, long long msg_id UU) { unread_messages ++; print_message (M); update_prompt (); + if (M->date > last_date) { + last_date = M->date; + } } void work_update_short_chat_message (struct connection *c UU, long long msg_id UU) { @@ -1081,6 +1109,9 @@ void work_update_short_chat_message (struct connection *c UU, long long msg_id U unread_messages ++; print_message (M); update_prompt (); + if (M->date > last_date) { + last_date = M->date; + } } void work_container (struct connection *c, long long msg_id UU) { @@ -1211,6 +1242,10 @@ void work_detained_info (struct connection *c UU, long long msg_id UU) { fetch_int (); // status } +void work_updates_to_long (struct connection *c UU, long long msg_id UU) { + assert (fetch_int () == (int)CODE_updates_too_long); + do_get_difference (); +} void rpc_execute_answer (struct connection *c, long long msg_id UU) { if (verbosity >= 5) { logprintf ("rpc_execute_answer: fd=%d\n", c->fd); @@ -1254,6 +1289,9 @@ void rpc_execute_answer (struct connection *c, long long msg_id UU) { case CODE_msg_detained_info: work_detained_info (c, msg_id); return; + case CODE_updates_too_long: + work_updates_to_long (c, msg_id); + return; } logprintf ( "Unknown message: \n"); hexdump_in (); diff --git a/mtproto-client.h b/mtproto-client.h index 3958b4e..9e44df2 100644 --- a/mtproto-client.h +++ b/mtproto-client.h @@ -23,4 +23,5 @@ void on_start (void); long long encrypt_send_message (struct connection *c, int *msg, int msg_ints, int useful); void dc_authorize (struct dc *DC); +void work_update (struct connection *c, long long msg_id); #endif diff --git a/mtproto-common.c b/mtproto-common.c index 64f635a..201ab68 100644 --- a/mtproto-common.c +++ b/mtproto-common.c @@ -37,6 +37,9 @@ #include "mtproto-common.h" #include "interface.h" +int __packet_buffer[PACKET_BUFFER_SIZE], *packet_ptr; +int *packet_buffer = __packet_buffer + 16; + long long rsa_encrypted_chunks, rsa_decrypted_chunks; BN_CTX *BN_ctx; diff --git a/mtproto-common.h b/mtproto-common.h index 1b93d31..94c97ac 100644 --- a/mtproto-common.h +++ b/mtproto-common.h @@ -83,6 +83,7 @@ #define MAX_MESSAGE_INTS 1048576 #define MAX_PROTO_MESSAGE_INTS 1048576 +#define PACKET_BUFFER_SIZE (16384 * 100 + 16) // temp fix #pragma pack(push,4) struct encrypted_message { // unencrypted header @@ -219,8 +220,8 @@ void prng_seed (const char *password_filename, int password_length); int serialize_bignum (BIGNUM *b, char *buffer, int maxlen); long long compute_rsa_key_fingerprint (RSA *key); -#define PACKET_BUFFER_SIZE (16384 * 100) // temp fix -int packet_buffer[PACKET_BUFFER_SIZE], *packet_ptr; +extern int *packet_buffer; +extern int *packet_ptr; static inline void out_ints (int *what, int len) { assert (packet_ptr + len <= packet_buffer + PACKET_BUFFER_SIZE); @@ -262,6 +263,9 @@ static inline void out_bignum (BIGNUM *n) { extern int *in_ptr, *in_end; void fetch_pts (void); +void fetch_qts (void); +void fetch_date (void); +void fetch_seq (void); static inline int prefetch_strlen (void) { if (in_ptr >= in_end) { return -1; diff --git a/net.h b/net.h index e9a4aa2..6061e7b 100644 --- a/net.h +++ b/net.h @@ -26,7 +26,7 @@ struct dc; //#define TG_SERVER "95.142.192.66" #define TG_APP_HASH "36722c72256a24c1225de00eb6a1ca74" #define TG_APP_ID 2899 -#define TG_BUILD "200" +#define TG_BUILD "203" #define TG_VERSION "0.01-beta" @@ -79,6 +79,9 @@ struct dc { #define DC_SERIALIZED_MAGIC 0x64582faa #define DC_SERIALIZED_MAGIC_V2 0x94032abb +#define STATE_FILE_MAGIC 0x84217a0d +#define SECRET_CHAT_FILE_MAGIC 0xa9840add + struct dc_serialized { int magic; int port; diff --git a/queries.c b/queries.c index 5e1b087..1871edd 100644 --- a/queries.c +++ b/queries.c @@ -37,6 +37,12 @@ #include "structures.h" #include "interface.h" #include "net.h" +#include +#include +#include +#include + +#define sha1 SHA1 char *get_downloads_directory (void); int verbosity; @@ -276,6 +282,8 @@ int new_dc_num; extern struct dc *DC_list[]; extern struct dc *DC_working; +/* {{{ Get config */ + int help_get_config_on_answer (struct query *q UU) { assert (fetch_int () == CODE_config); fetch_int (); @@ -323,7 +331,9 @@ void do_help_get_config (void) { out_int (CODE_help_get_config); send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &help_get_config_methods, 0); } +/* }}} */ +/* {{{ Send code */ char *phone_code_hash; int send_code_on_answer (struct query *q UU) { assert (fetch_int () == CODE_auth_sent_code); @@ -407,8 +417,9 @@ void do_send_code (const char *user) { net_loop (0, code_is_sent); assert (want_dc_num == -1); } +/* }}} */ - +/* {{{ Check phone */ int check_phone_result; int cr_f (void) { return check_phone_result >= 0; @@ -460,7 +471,9 @@ int do_auth_check_phone (const char *user) { net_loop (0, cr_f); return check_phone_result; } +/* }}} */ +/* {{{ Nearest DC */ int nearest_dc_num; int nr_f (void) { return nearest_dc_num >= 0; @@ -497,7 +510,9 @@ int do_get_nearest_dc (void) { net_loop (0, nr_f); return nearest_dc_num; } +/* }}} */ +/* {{{ Sign in / Sign up */ int sign_in_ok; int sign_in_is_ok (void) { return sign_in_ok; @@ -554,7 +569,9 @@ int do_send_code_result_auth (const char *code, const char *first_name, const ch net_loop (0, sign_in_is_ok); return sign_in_ok; } +/* }}} */ +/* {{{ Get contacts */ extern char *user_list[]; int get_contacts_on_answer (struct query *q UU) { @@ -612,18 +629,106 @@ void do_update_contact_list (void) { out_string (""); send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_contacts_methods, 0); } +/* }}} */ +/* {{{ Encrypt decrypted */ +int *encr_extra; +int *encr_ptr; +int *encr_end; + +char *encrypt_decrypted_message (struct secret_chat *E) { + static int msg_key[4]; + static unsigned char sha1a_buffer[20]; + static unsigned char sha1b_buffer[20]; + static unsigned char sha1c_buffer[20]; + static unsigned char sha1d_buffer[20]; + int x = *(encr_ptr); + assert (x >= 0 && !(x & 3)); + sha1 ((void *)encr_ptr, 4 + x, sha1a_buffer); + memcpy (msg_key, sha1a_buffer + 4, 16); + + static unsigned char buf[64]; + memcpy (buf, msg_key, 16); + memcpy (buf + 16, E->key, 32); + sha1 (buf, 48, sha1a_buffer); + + memcpy (buf, E->key + 8, 16); + memcpy (buf + 16, msg_key, 16); + memcpy (buf + 32, E->key + 12, 16); + sha1 (buf, 48, sha1b_buffer); + + memcpy (buf, E->key + 16, 32); + memcpy (buf + 32, msg_key, 16); + sha1 (buf, 48, sha1c_buffer); + + memcpy (buf, msg_key, 16); + memcpy (buf + 16, E->key + 24, 32); + sha1 (buf, 48, sha1d_buffer); + + static unsigned char key[32]; + memcpy (key, sha1a_buffer + 0, 8); + memcpy (key + 8, sha1b_buffer + 8, 12); + memcpy (key + 20, sha1c_buffer + 4, 12); + + static unsigned char iv[32]; + memcpy (iv, sha1a_buffer + 8, 12); + memcpy (iv + 12, sha1b_buffer + 0, 8); + memcpy (iv + 20, sha1c_buffer + 16, 4); + memcpy (iv + 24, sha1d_buffer + 0, 8); + + AES_KEY aes_key; + AES_set_encrypt_key (key, 256, &aes_key); + AES_ige_encrypt ((void *)encr_ptr, (void *)encr_ptr, 4 * (encr_end - encr_ptr), &aes_key, iv, 1); + + return (void *)msg_key; +} + +void encr_start (void) { + encr_extra = packet_ptr; + packet_ptr += 1; // str len + packet_ptr += 2; // fingerprint + packet_ptr += 4; // msg_key + packet_ptr += 1; // len +} + + +void encr_finish (struct secret_chat *E) { + int l = packet_ptr - (encr_extra + 8); + while (((packet_ptr - encr_extra) - 3) & 3) { + out_int (mrand48 ()); + } + + *encr_extra = ((packet_ptr - encr_extra) - 1) * 4 * 256 + 0xfe; + encr_extra ++; + *(long long *)encr_extra = E->key_fingerprint; + encr_extra += 2; + encr_extra[4] = l * 4; + encr_ptr = encr_extra + 4; + encr_end = packet_ptr; + memcpy (encr_extra, encrypt_decrypted_message (E), 16); +} +/* }}} */ + +/* {{{ Seng msg (plain text) */ +int msg_send_encr_on_answer (struct query *q UU) { + assert (fetch_int () == CODE_messages_sent_encrypted_message); + logprintf ("Sent\n"); + struct message *M = q->extra; + M->date = fetch_int (); + message_insert (M); + return 0; +} int msg_send_on_answer (struct query *q UU) { assert (fetch_int () == (int)CODE_messages_sent_message); int id = fetch_int (); // id - int date = fetch_int (); // date + fetch_date (); fetch_pts (); - int seq = fetch_int (); // seq + fetch_seq (); struct message *M = q->extra; M->id = id; message_insert (M); - logprintf ("Sent: id = %d, date = %d, seq = %d\n", id, date, seq); + logprintf ("Sent: id = %d\n", id); return 0; } @@ -631,10 +736,71 @@ struct query_methods msg_send_methods = { .on_answer = msg_send_on_answer }; +struct query_methods msg_send_encr_methods = { + .on_answer = msg_send_encr_on_answer +}; + int out_message_num; int our_id; void out_peer_id (peer_id_t id); + +void do_send_encr_message (peer_id_t id, const char *msg, int len) { + peer_t *P = user_chat_get (id); + if (!P) { + logprintf ("Can not send to unknown encrypted chat\n"); + return; + } + if (P->encr_chat.state != sc_ok) { + logprintf ("Chat is not yet initialized\n"); + return; + } + clear_packet (); + out_int (CODE_messages_send_encrypted); + out_int (CODE_input_encrypted_chat); + out_int (get_peer_id (id)); + out_long (P->encr_chat.access_hash); + if (!out_message_num) { + out_message_num = -lrand48 (); + } + out_long ((--out_message_num) - (4ll << 32)); + encr_start (); + //out_int (CODE_decrypted_message_layer); + //out_int (8); + out_int (CODE_decrypted_message); + out_long ((out_message_num) - (4ll << 32)); + static int buf[4]; + int i; + for (i = 0; i < 3; i++) { + buf[i] = mrand48 (); + } + out_cstring ((void *)buf, 16); + out_cstring ((void *)msg, len); + out_int (CODE_decrypted_message_media_empty); + encr_finish (&P->encr_chat); + + struct message *M = malloc (sizeof (*M)); + memset (M, 0, sizeof (*M)); + M->from_id = MK_USER (our_id); + M->to_id = id; + M->unread = 1; + M->message = malloc (len + 1); + memcpy (M->message, msg, len); + M->message[len] = 0; + M->message_len = len; + M->out = 1; + M->media.type = CODE_message_media_empty; + M->id = (out_message_num) - (4ll << 32); + M->date = time (0); + + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &msg_send_encr_methods, M); + print_message (M); +} + void do_send_message (peer_id_t id, const char *msg, int len) { + if (get_peer_type (id) == PEER_ENCR_CHAT) { + do_send_encr_message (id, msg, len); + return; + } if (!out_message_num) { out_message_num = -lrand48 (); } @@ -659,7 +825,9 @@ void do_send_message (peer_id_t id, const char *msg, int len) { send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &msg_send_methods, M); print_message (M); } +/* }}} */ +/* {{{ Send text file */ void do_send_text (peer_id_t id, char *file_name) { int fd = open (file_name, O_RDONLY); if (fd < 0) { @@ -681,19 +849,29 @@ void do_send_text (peer_id_t id, char *file_name) { close (fd); } } +/* }}} */ int mark_read_on_receive (struct query *q UU) { assert (fetch_int () == (int)CODE_messages_affected_history); - fetch_int (); // pts - fetch_int (); // seq + fetch_pts (); + fetch_seq (); fetch_int (); // offset return 0; } +int mark_read_encr_on_receive (struct query *q UU) { + fetch_bool (); + return 0; +} + struct query_methods mark_read_methods = { .on_answer = mark_read_on_receive }; +struct query_methods mark_read_encr_methods = { + .on_answer = mark_read_encr_on_receive +}; + void do_messages_mark_read (peer_id_t id, int max_id) { clear_packet (); out_int (CODE_messages_read_history); @@ -703,6 +881,34 @@ void do_messages_mark_read (peer_id_t id, int max_id) { send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &mark_read_methods, 0); } +void do_messages_mark_read_encr (peer_id_t id, long long access_hash, int last_time) { + clear_packet (); + out_int (CODE_messages_read_encrypted_history); + out_int (CODE_input_encrypted_chat); + out_int (get_peer_id (id)); + out_long (access_hash); + out_int (last_time); + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &mark_read_encr_methods, 0); +} + +void do_mark_read (peer_id_t id) { + peer_t *P = user_chat_get (id); + if (!P) { + rprintf ("Unknown peer\n"); + return; + } + if (!P->last) { + rprintf ("Unknown last peer message\n"); + return; + } + if (get_peer_type (id) == PEER_USER || get_peer_type (id) == PEER_CHAT) { + do_messages_mark_read (id, P->last->id); + return; + } + assert (get_peer_type (id) == PEER_ENCR_CHAT); + do_messages_mark_read_encr (id, P->encr_chat.access_hash, P->last->date); +} + int get_history_on_answer (struct query *q UU) { static struct message *ML[10000]; int i; @@ -919,8 +1125,8 @@ int send_file_on_answer (struct query *q UU) { for (i = 0; i < n; i++) { fetch_alloc_user (); } - fetch_int (); // pts - fetch_int (); // seq + fetch_pts (); + fetch_seq (); print_message (M); return 0; } @@ -1029,8 +1235,8 @@ int fwd_msg_on_answer (struct query *q UU) { for (i = 0; i < n; i++) { fetch_alloc_user (); } - fetch_int (); // pts - fetch_int (); // seq + fetch_pts (); + fetch_seq (); print_message (M); return 0; } @@ -1041,7 +1247,7 @@ struct query_methods fwd_msg_methods = { void do_forward_message (peer_id_t id, int n) { clear_packet (); - out_int (CODE_invoke_with_layer3); + out_int (CODE_invoke_with_layer9); out_int (CODE_messages_forward_message); out_peer_id (id); out_int (n); @@ -1063,8 +1269,8 @@ int rename_chat_on_answer (struct query *q UU) { for (i = 0; i < n; i++) { fetch_alloc_user (); } - fetch_int (); // pts - fetch_int (); // seq + fetch_pts (); + fetch_seq (); print_message (M); return 0; } @@ -1509,3 +1715,241 @@ void do_msg_search (peer_id_t id, int from, int to, int limit, const char *s) { out_int (limit); send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &msg_search_methods, 0); } + +int send_encr_accept_on_answer (struct query *q UU) { + struct secret_chat *E = fetch_alloc_encrypted_chat (); + + if (E->state == sc_ok) { + print_start (); + push_color (COLOR_YELLOW); + printf ("Encrypted connection with "); + print_encr_chat_name (E->id, (void *)E); + printf (" established\n"); + pop_color (); + print_end (); + } else { + print_start (); + push_color (COLOR_YELLOW); + printf ("Encrypted connection with "); + print_encr_chat_name (E->id, (void *)E); + printf (" failed\n"); + pop_color (); + print_end (); + } + return 0; +} + +struct query_methods send_encr_accept_methods = { + .on_answer = send_encr_accept_on_answer +}; + +int encr_root; +unsigned char *encr_prime; +int encr_param_version; +BN_CTX *ctx; + +void do_send_accept_encr_chat (struct secret_chat *E, unsigned char *random) { + int i; + for (i = 0; i < 64; i++) { + *(((int *)random) + i) ^= mrand48 (); + } + BIGNUM *b = BN_bin2bn (random, 256, 0); + assert (b); + BIGNUM *g_a = BN_bin2bn (E->g_key, 256, 0); + assert (g_a); + if (!ctx) { + ctx = BN_CTX_new (); + BN_CTX_init (ctx); + } + BIGNUM *p = BN_bin2bn (encr_prime, 256, 0); + BIGNUM *r = BN_new (); + BN_init (r); + BN_mod_exp (r, g_a, b, p, ctx); + memset (E->key, 0, sizeof (E->key)); + BN_bn2bin (r, (void *)E->key); + for (i = 0; i < 64; i++) { + E->key[i] ^= *(((int *)E->nonce) + i); + } + static unsigned char sha_buffer[20]; + sha1 ((void *)E->key, 256, sha_buffer); + E->key_fingerprint = *(long long *)(sha_buffer + 12); + + clear_packet (); + out_int (CODE_messages_accept_encryption); + out_int (CODE_input_encrypted_chat); + logprintf ("id = %d\n", get_peer_id (E->id)); + out_int (get_peer_id (E->id)); + out_long (E->access_hash); + + BN_set_word (g_a, encr_root); + BN_mod_exp (r, g_a, b, p, ctx); + static unsigned char buf[256]; + memset (buf, 0, sizeof (buf)); + BN_bn2bin (r, buf); + out_cstring ((void *)buf, 256); + + out_long (E->key_fingerprint); + BN_clear_free (b); + BN_clear_free (g_a); + BN_clear_free (p); + BN_clear_free (r); + + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &send_encr_accept_methods, E); +} + +int get_dh_config_on_answer (struct query *q UU) { + unsigned x = fetch_int (); + assert (x == CODE_messages_dh_config || x == CODE_messages_dh_config_not_modified); + if (x == CODE_messages_dh_config) { + encr_root = fetch_int (); + if (encr_prime) { free (encr_prime); } + int l = prefetch_strlen (); + assert (l == 256); + encr_prime = (void *)fetch_str_dup (); + encr_param_version = fetch_int (); + } + int l = prefetch_strlen (); + assert (l == 256); + unsigned char *random = (void *)fetch_str_dup (); + if (q->extra) { + do_send_accept_encr_chat (q->extra, random); + free (random); + } else { + free (random); + } + return 0; +} + +struct query_methods get_dh_config_methods = { + .on_answer = get_dh_config_on_answer +}; + +void do_accept_encr_chat_request (struct secret_chat *E) { + assert (E->state == sc_request); + + clear_packet (); + out_int (CODE_messages_get_dh_config); + out_int (encr_param_version); + out_int (256); + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_dh_config_methods, E); +} + +int unread_messages; +int difference_got; +int seq, pts, qts, last_date; +int get_state_on_answer (struct query *q UU) { + assert (fetch_int () == (int)CODE_updates_state); + pts = fetch_int (); + qts = fetch_int (); + last_date = fetch_int (); + seq = fetch_int (); + unread_messages = fetch_int (); + write_state_file (); + difference_got = 1; + return 0; +} + +int get_difference_on_answer (struct query *q UU) { + unsigned x = fetch_int (); + if (x == CODE_updates_difference_empty) { + fetch_date (); + fetch_seq (); + difference_got = 1; + } else if (x == CODE_updates_difference || x == CODE_updates_difference_slice) { + int n, i; + assert (fetch_int () == CODE_vector); + n = fetch_int (); + static struct message *ML[10000]; + int ml_pos = 0; + for (i = 0; i < n; i++) { + if (ml_pos < 10000) { + ML[ml_pos ++] = fetch_alloc_message (); + } else { + fetch_alloc_message (); + } + } + assert (fetch_int () == CODE_vector); + n = fetch_int (); + for (i = 0; i < n; i++) { + if (ml_pos < 10000) { + ML[ml_pos ++] = fetch_alloc_encrypted_message (); + } else { + fetch_alloc_encrypted_message (); + } + } + assert (fetch_int () == CODE_vector); + n = fetch_int (); + for (i = 0; i < n; i++) { + work_update (0, 0); + } + assert (fetch_int () == CODE_vector); + n = fetch_int (); + for (i = 0; i < n; i++) { + fetch_alloc_chat (); + } + assert (fetch_int () == CODE_vector); + n = fetch_int (); + for (i = 0; i < n; i++) { + fetch_alloc_user (); + } + assert (fetch_int () == (int)CODE_updates_state); + pts = fetch_int (); + qts = fetch_int (); + last_date = fetch_int (); + seq = fetch_int (); + unread_messages = fetch_int (); + write_state_file (); + for (i = 0; i < ml_pos; i++) { + print_message (ML[i]); + } + if (x == CODE_updates_difference_slice) { + do_get_difference (); + } else { + difference_got = 1; + } + } else { + assert (0); + } + return 0; +} + +struct query_methods get_state_methods = { + .on_answer = get_state_on_answer +}; + +struct query_methods get_difference_methods = { + .on_answer = get_difference_on_answer +}; + +void do_get_difference (void) { + difference_got = 0; + clear_packet (); + out_int (CODE_invoke_with_layer9); + out_int (CODE_init_connection); + out_int (TG_APP_ID); + if (allow_send_linux_version) { + struct utsname st; + uname (&st); + out_string (st.machine); + static char buf[1000000]; + sprintf (buf, "%s %s %s", st.sysname, st.release, st.version); + out_string (buf); + out_string (TG_VERSION " (build " TG_BUILD ")"); + out_string ("En"); + } else { + out_string ("x86"); + out_string ("Linux"); + out_string (TG_VERSION); + out_string ("en"); + } + if (seq > 0) { + out_int (CODE_updates_get_difference); + out_int (pts); + out_int (last_date); + out_int (qts); + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_difference_methods, 0); + } else { + out_int (CODE_updates_get_state); + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_state_methods, 0); + } +} diff --git a/queries.h b/queries.h index 8a9cb9b..beff3a8 100644 --- a/queries.h +++ b/queries.h @@ -94,5 +94,8 @@ void do_import_auth (int num); void do_export_auth (int num); void do_add_contact (const char *phone, int phone_len, const char *first_name, int first_name_len, const char *last_name, int last_name_len, int force); void do_msg_search (peer_id_t id, int from, int to, int limit, const char *s); +void do_accept_encr_chat_request (struct secret_chat *E); +void do_get_difference (void); +void do_mark_read (peer_id_t id); #endif diff --git a/structures.c b/structures.c index 89055e6..c5a1035 100644 --- a/structures.c +++ b/structures.c @@ -32,6 +32,10 @@ int verbosity; peer_t *Peers[MAX_USER_NUM]; +int peer_num; +int encr_chats_allocated; +int geo_chats_allocated; + void fetch_file_location (struct file_location *loc) { int x = fetch_int (); if (x == CODE_file_location_unavailable) { @@ -68,8 +72,63 @@ void fetch_user_status (struct user_status *S) { } int our_id; -int user_num; -int chat_num; + +char *create_print_name (peer_id_t id, const char *a1, const char *a2, const char *a3, const char *a4) { + const char *d[4]; + d[0] = a1; d[1] = a2; d[2] = a3; d[3] = a4; + static char buf[10000]; + int i; + int p = 0; + for (i = 0; i < 4; i++) { + if (d[i] && strlen (d[i])) { + p += snprintf (buf + p, 9999 - p, "%s%s", p ? "_" : "", d[i]); + assert (p < 9990); + } + } + char *s = buf; + while (*s) { + if (*s == ' ') { *s = '_'; } + s++; + } + s = buf; + int cc = 0; + while (1) { + int ok = 1; + int i; + for (i = 0; i < peer_num; i++) { + if (cmp_peer_id (Peers[i]->id, id) && Peers[i]->print_name && !strcmp (Peers[i]->print_name, s)) { + ok = 0; + break; + } + } + if (ok) { + break; + } + cc ++; + assert (cc <= 99); + if (cc == 1) { + int l = strlen (s); + s[l + 2] = 0; + s[l] = '#'; + s[l + 1] = '1'; + } else if (cc == 10) { + int l = strlen (s); + s[l + 1] = 0; + s[l] = '0'; + s[l - 1] = '1'; + } else { + int l = strlen (s); + s[l - 1] ++; + int cc = l - 1; + while (s[cc] > '9') { + s[cc] = '0'; + s[cc - 1] ++; + cc --; + } + } + } + return strdup (s); +} void fetch_user (struct user *U) { unsigned x = fetch_int (); @@ -92,67 +151,8 @@ void fetch_user (struct user *U) { if (U->print_name) { free (U->print_name); } U->first_name = fetch_str_dup (); U->last_name = fetch_str_dup (); - if (!strlen (U->first_name)) { - if (!strlen (U->last_name)) { - U->print_name = strdup ("none"); - } else { - U->print_name = strdup (U->last_name); - } - } else { - if (!strlen (U->last_name)) { - U->print_name = strdup (U->first_name); - } else { - U->print_name = malloc (strlen (U->first_name) + strlen (U->last_name) + 2); - sprintf (U->print_name, "%s_%s", U->first_name, U->last_name); - } - } - char *s = U->print_name; - while (*s) { - if (*s == ' ') { *s = '_'; } - s++; - } - int cc = 0; - while (1) { - int ok = 1; - int i; - for (i = 0; i < user_num + chat_num; i++) { - if (Peers[i] != (void *)U && Peers[i]->print_name && !strcmp (Peers[i]->print_name, U->print_name)) { - ok = 0; - break; - } - } - if (ok) { - break; - } - cc ++; - assert (cc <= 99); - if (cc == 1) { - int l = strlen (U->print_name); - char *s = malloc (l + 3); - memcpy (s, U->print_name, l); - s[l + 2] = 0; - s[l] = '#'; - s[l + 1] = '1'; - free (U->print_name); - U->print_name = s; - } else if (cc == 10) { - int l = strlen (U->print_name); - char *s = malloc (l + 2); - memcpy (s, U->print_name, l); - s[l + 1] = 0; - s[l] = '0'; - s[l - 1] = '1'; - free (U->print_name); - U->print_name = s; - } else { - int l = strlen (U->print_name); - U->print_name[l - 1] ++; - if (U->print_name[l - 1] > '9') { - U->print_name[l - 1] = '0'; - U->print_name[l - 2] ++; - } - } - } + + U->print_name = create_print_name (U->id, U->first_name, U->last_name, 0, 0); if (x == CODE_user_deleted) { U->flags |= FLAG_DELETED; return; @@ -197,47 +197,96 @@ void fetch_encrypted_chat (struct secret_chat *U) { assert (x == CODE_encrypted_chat_empty || x == CODE_encrypted_chat_waiting || x == CODE_encrypted_chat_requested || x == CODE_encrypted_chat || x == CODE_encrypted_chat_discarded); U->id = MK_ENCR_CHAT (fetch_int ()); U->flags &= ~(FLAG_EMPTY | FLAG_DELETED); + enum secret_chat_state old_state = U->state; if (x == CODE_encrypted_chat_empty) { U->state = sc_none; U->flags |= FLAG_EMPTY; + if (U->state != old_state) { + write_secret_chat_file (); + } return; } if (x == CODE_encrypted_chat_discarded) { U->state = sc_deleted; U->flags |= FLAG_DELETED; + if (U->state != old_state) { + write_secret_chat_file (); + } return; } U->access_hash = fetch_long (); U->date = fetch_int (); U->admin_id = fetch_int (); U->user_id = fetch_int () + U->admin_id - our_id; + if (U->print_name) { free (U->print_name); } + + peer_t *P = user_chat_get (MK_USER (U->user_id)); + if (P) { + U->print_name = create_print_name (U->id, "!", P->user.first_name, P->user.last_name, 0); + } else { + static char buf[100]; + sprintf (buf, "user#%d", U->user_id); + U->print_name = create_print_name (U->id, "!", buf, 0, 0); + } + if (x == CODE_encrypted_chat_waiting) { U->state = sc_waiting; } else if (x == CODE_encrypted_chat_requested) { U->state = sc_request; if (!U->g_key) { - U->g_key = malloc (256); + U->g_key = malloc (256); } + memset (U->g_key, 0, 256); if (!U->nonce) { U->nonce = malloc (256); } - assert (prefetch_strlen () == 256); - memcpy (U->g_key, fetch_str (256), 256); - assert (prefetch_strlen () == 256); - memcpy (U->nonce, fetch_str (256), 256); + memset (U->nonce, 0, 256); + int l = prefetch_strlen (); + char *s = fetch_str (l); + if (l < 256) { + memcpy (U->g_key + 256 - l, s, l); + } else { + memcpy (U->g_key, s + (l - 256), 256); + } + l = prefetch_strlen (); + s = fetch_str (l); + if (l < 256) { + memcpy (U->nonce + 256 - l, s, l); + } else { + memcpy (U->nonce, s + (l - 256), 256); + } } else { U->state = sc_ok; if (!U->g_key) { U->g_key = malloc (256); } + memset (U->g_key, 0, 256); if (!U->nonce) { U->nonce = malloc (256); } - assert (prefetch_strlen () == 256); - memcpy (U->g_key, fetch_str (256), 256); - assert (prefetch_strlen () == 256); - memcpy (U->nonce, fetch_str (256), 256); - U->key_fingerprint = fetch_long (); + memset (U->nonce, 0, 256); + int l = prefetch_strlen (); + char *s = fetch_str (l); + if (l < 256) { + memcpy (U->g_key + 256 - l, s, l); + } else { + memcpy (U->g_key, s + (l - 256), 256); + } + l = prefetch_strlen (); + s = fetch_str (l); + if (l < 256) { + memcpy (U->nonce + 256 - l, s, l); + } else { + memcpy (U->nonce, s + (l - 256), 256); + } + if (!U->key_fingerprint) { + U->key_fingerprint = fetch_long (); + } else { + assert (U->key_fingerprint == fetch_long ()); + } + } + if (U->state != old_state) { + write_secret_chat_file (); } } @@ -294,12 +343,7 @@ void fetch_chat (struct chat *C) { if (C->title) { free (C->title); } if (C->print_title) { free (C->print_title); } C->title = fetch_str_dup (); - C->print_title = strdup (C->title); - char *s = C->print_title; - while (*s) { - if (*s == ' ') { *s = '_'; } - s ++; - } + C->print_title = create_print_name (C->id, C->title, 0, 0, 0); if (x == CODE_chat) { unsigned y = fetch_int (); if (y == CODE_chat_photo_empty) { @@ -499,9 +543,9 @@ void fetch_message_short (struct message *M) { M->to_id = MK_USER (our_id); M->from_id = MK_USER (fetch_int ()); M->message = fetch_str_dup (); - fetch_int (); // pts + fetch_pts (); M->date = fetch_int (); - fetch_int (); // seq + fetch_seq (); M->media.type = CODE_message_media_empty; M->unread = 1; } @@ -512,9 +556,9 @@ void fetch_message_short_chat (struct message *M) { M->from_id = MK_USER (fetch_int ()); M->to_id = MK_CHAT (fetch_int ()); M->message = fetch_str_dup (); - fetch_int (); // pts + fetch_pts (); M->date = fetch_int (); - fetch_int (); // seq + fetch_seq (); M->media.type = CODE_message_media_empty; M->unread = 1; } @@ -554,7 +598,7 @@ void fetch_message_media_encrypted (struct message_media *M) { memset (M, 0, sizeof (*M)); unsigned x = fetch_int (); int l; - switch (M->type) { + switch (x) { case CODE_decrypted_message_media_empty: M->type = CODE_message_media_empty; break; @@ -567,15 +611,25 @@ void fetch_message_media_encrypted (struct message_media *M) { M->encr_photo.w = fetch_int (); M->encr_photo.h = fetch_int (); M->encr_photo.size = fetch_int (); - l = fetch_int (); - assert (l > 0); - M->encr_photo.key = malloc (l); - memcpy (M->encr_photo.key, fetch_str (l), l); - l = fetch_int (); + l = prefetch_strlen (); assert (l > 0); - M->encr_photo.iv = malloc (l); - memcpy (M->encr_photo.iv, fetch_str (l), l); + M->encr_photo.key = malloc (32); + memset (M->encr_photo.key, 0, 32); + if (l <= 32) { + memcpy (M->encr_photo.key + (32 - l), fetch_str (l), l); + } else { + memcpy (M->encr_photo.key, fetch_str (l) + (l - 32), 32); + } + M->encr_photo.iv = malloc (32); + l = prefetch_strlen (); + assert (l > 0); + memset (M->encr_photo.iv, 0, 32); + if (l <= 32) { + memcpy (M->encr_photo.iv + (32 - l), fetch_str (l), l); + } else { + memcpy (M->encr_photo.iv, fetch_str (l) + (l - 32), 32); + } break; case CODE_decrypted_message_media_video: M->type = x; @@ -587,15 +641,25 @@ void fetch_message_media_encrypted (struct message_media *M) { M->encr_video.h = fetch_int (); M->encr_video.size = fetch_int (); M->encr_video.duration = fetch_int (); - l = fetch_int (); - assert (l > 0); - M->encr_video.key = malloc (l); - memcpy (M->encr_video.key, fetch_str (l), l); - l = fetch_int (); + l = prefetch_strlen (); assert (l > 0); - M->encr_video.iv = malloc (l); - memcpy (M->encr_video.iv, fetch_str (l), l); + M->encr_video.key = malloc (32); + memset (M->encr_photo.key, 0, 32); + if (l <= 32) { + memcpy (M->encr_video.key + (32 - l), fetch_str (l), l); + } else { + memcpy (M->encr_video.key, fetch_str (l) + (l - 32), 32); + } + M->encr_video.iv = malloc (32); + l = prefetch_strlen (); + assert (l > 0); + memset (M->encr_video.iv, 0, 32); + if (l <= 32) { + memcpy (M->encr_video.iv + (32 - l), fetch_str (l), l); + } else { + memcpy (M->encr_video.iv, fetch_str (l) + (l - 32), 32); + } break; /* case CODE_decrypted_message_media_file: M->type = x; @@ -719,24 +783,26 @@ int decrypt_encrypted_message (struct secret_chat *E) { memcpy (buf + 16, E->key + 24, 32); sha1 (buf, 48, sha1d_buffer); - static unsigned char iv[32]; - memcpy (iv, sha1a_buffer + 0, 8); - memcpy (iv + 8, sha1b_buffer + 8, 12); - memcpy (iv + 20, sha1c_buffer + 4, 12); - static unsigned char key[32]; - memcpy (key, sha1a_buffer + 8, 12); - memcpy (key + 12, sha1b_buffer + 0, 8); - memcpy (key + 20, sha1c_buffer + 16, 4); - memcpy (key + 24, sha1d_buffer + 0, 8); + memcpy (key, sha1a_buffer + 0, 8); + memcpy (key + 8, sha1b_buffer + 8, 12); + memcpy (key + 20, sha1c_buffer + 4, 12); + + static unsigned char iv[32]; + memcpy (iv, sha1a_buffer + 8, 12); + memcpy (iv + 12, sha1b_buffer + 0, 8); + memcpy (iv + 20, sha1c_buffer + 16, 4); + memcpy (iv + 24, sha1d_buffer + 0, 8); AES_KEY aes_key; AES_set_decrypt_key (key, 256, &aes_key); AES_ige_encrypt ((void *)decr_ptr, (void *)decr_ptr, 4 * (decr_end - decr_ptr), &aes_key, iv, 0); - sha1 ((void *)decr_ptr, 4 * (decr_end - decr_ptr), sha1a_buffer); + int x = *(decr_ptr); + assert (x >= 0 && !(x & 3)); + sha1 ((void *)decr_ptr, 4 + x, sha1a_buffer); - if (memcmp (sha1a_buffer, msg_key, 16)) { + if (memcmp (sha1a_buffer + 4, msg_key, 16)) { logprintf ("Sha1 mismatch\n"); return -1; } @@ -747,40 +813,72 @@ void fetch_encrypted_message (struct message *M) { memset (M, 0, sizeof (*M)); unsigned x = fetch_int (); assert (x == CODE_encrypted_message || x == CODE_encrypted_message_service); - peer_id_t chat = MK_ENCR_CHAT (fetch_int ()); + unsigned sx = x; M->id = fetch_long (); + peer_id_t chat = MK_ENCR_CHAT (fetch_int ()); + M->to_id = chat; peer_t *P = user_chat_get (chat); if (!P) { logprintf ("Encrypted message to unknown chat. Dropping\n"); + M->flags |= FLAG_EMPTY; } M->date = fetch_int (); + int len = prefetch_strlen (); - assert (!(len & 15)); + assert ((len & 15) == 8); decr_ptr = (void *)fetch_str (len); - decr_end = in_ptr; + decr_end = decr_ptr + (len / 4); M->flags |= FLAG_ENCRYPTED; + int ok = 0; + if (P) { + if (*(long long *)decr_ptr != P->encr_chat.key_fingerprint) { + logprintf ("Encrypted message with bad fingerprint to chat %s\n", P->print_name); + P = 0; + } + decr_ptr += 2; + } if (P && decrypt_encrypted_message (&P->encr_chat) >= 0) { + ok = 1; + int *save_in_ptr = in_ptr; + int *save_in_end = in_end; in_ptr = decr_ptr; + int l = fetch_int (); + in_end = in_ptr + l; unsigned x = fetch_int (); if (x == CODE_decrypted_message_layer) { int layer = fetch_int (); assert (layer >= 0); x = fetch_int (); } - assert (x == CODE_decrypted_message); + assert (x == CODE_decrypted_message || x == CODE_decrypted_message_service); assert (M->id = fetch_long ()); - int l = prefetch_strlen (); + l = prefetch_strlen (); fetch_str (l); // random_bytes - M->from_id = MK_USER (fetch_int ()); - M->date = fetch_int (); if (x == CODE_decrypted_message) { M->message = fetch_str_dup (); - fetch_encrypted_message_file (&M->media); + fetch_message_media_encrypted (&M->media); } else { assert (fetch_int () == (int)CODE_decrypted_message_action_set_message_t_t_l); P->encr_chat.ttl = fetch_int (); + M->service = 1; } + in_ptr = save_in_ptr; + in_end = save_in_end; + } + + if (sx == CODE_encrypted_message) { + if (ok) { + fetch_encrypted_message_file (&M->media); + } else { + x = fetch_int (); + if (x == CODE_encrypted_file) { + fetch_skip (7); + } else { + assert (x == CODE_encrypted_file_empty); + } + M->media.type = CODE_message_media_empty; + } } } @@ -793,7 +891,8 @@ void fetch_encrypted_message_file (struct message_media *M) { assert (M->type == CODE_decrypted_message_media_photo || M->type == CODE_decrypted_message_media_video); M->encr_photo.id = fetch_long (); M->encr_photo.access_hash = fetch_long (); - assert (M->encr_photo.size == fetch_int ()); + //assert (M->encr_photo.size == fetch_int ()); + M->encr_photo.size = fetch_int (); // Why it is not the same? M->encr_photo.dc_id = fetch_int (); M->encr_photo.key_fingerprint = fetch_int (); @@ -813,8 +912,6 @@ DEFINE_TREE(message,struct message *,id_cmp,0) struct tree_peer *peer_tree; struct tree_message *message_tree; -int chat_num; -int user_num; int users_allocated; int chats_allocated; int messages_allocated; @@ -837,7 +934,7 @@ struct user *fetch_alloc_user (void) { memset (U, 0, sizeof (*U)); fetch_user (&U->user); peer_tree = tree_insert_peer (peer_tree, U, lrand48 ()); - Peers[chat_num + (user_num ++)] = U; + Peers[peer_num ++] = U; return &U->user; } } @@ -850,16 +947,22 @@ struct secret_chat *fetch_alloc_encrypted_chat (void) { fetch_encrypted_chat (&U->encr_chat); return &U->encr_chat; } else { - chats_allocated ++; + encr_chats_allocated ++; U = malloc (sizeof (*U)); memset (U, 0, sizeof (*U)); fetch_encrypted_chat (&U->encr_chat); peer_tree = tree_insert_peer (peer_tree, U, lrand48 ()); - Peers[(chat_num ++) + user_num] = U; + Peers[peer_num ++] = U; return &U->encr_chat; } } +void insert_encrypted_chat (peer_t *P) { + encr_chats_allocated ++; + peer_tree = tree_insert_peer (peer_tree, P, lrand48 ()); + Peers[peer_num ++] = P; +} + struct user *fetch_alloc_user_full (void) { int data[3]; prefetch_data (data, 12); @@ -874,7 +977,7 @@ struct user *fetch_alloc_user_full (void) { U->id = MK_USER (data[2]); peer_tree = tree_insert_peer (peer_tree, U, lrand48 ()); fetch_user_full (&U->user); - Peers[chat_num + (user_num ++ )] = U; + Peers[peer_num ++] = U; return &U->user; } } @@ -938,6 +1041,8 @@ void free_message_media (struct message_media *M) { free (M->encr_video.key); free (M->encr_video.iv); return; + case 0: + break; default: logprintf ("%08x\n", M->type); assert (0); @@ -964,6 +1069,8 @@ void free_message_action (struct message_action *M) { break; case CODE_message_action_chat_delete_user: break; + case 0: + break; default: assert (0); } @@ -990,6 +1097,61 @@ void message_add_use (struct message *M) { M->prev_use->next_use = M; } +void message_add_peer (struct message *M) { + peer_id_t id; + if (!cmp_peer_id (M->to_id, MK_USER (our_id))) { + id = M->from_id; + } else { + id = M->to_id; + } + peer_t *P = user_chat_get (id); + if (!P) { + P = malloc (sizeof (*P)); + memset (P, 0, sizeof (*P)); + P->id = id; + P->flags = FLAG_EMPTY; + switch (get_peer_type (id)) { + case PEER_USER: + users_allocated ++; + break; + case PEER_CHAT: + chats_allocated ++; + break; + case PEER_GEO_CHAT: + geo_chats_allocated ++; + break; + case PEER_ENCR_CHAT: + encr_chats_allocated ++; + break; + } + peer_tree = tree_insert_peer (peer_tree, P, lrand48 ()); + Peers[peer_num ++] = P; + } + M->next = P->last; + if (M->next) { M->next->prev = M; } + M->prev = 0; + P->last = M; +} + +void message_del_peer (struct message *M) { + peer_id_t id; + if (!cmp_peer_id (M->to_id, MK_USER (our_id))) { + id = M->from_id; + } else { + id = M->to_id; + } + peer_t *P = user_chat_get (id); + if (M->prev) { + M->prev->next = M->next; + } + if (M->next) { + M->next->prev = M->prev; + } + if (P && P->last == M) { + P->last = M->next; + } +} + struct message *fetch_alloc_message (void) { struct message *M = malloc (sizeof (*M)); fetch_message (M); @@ -997,14 +1159,17 @@ struct message *fetch_alloc_message (void) { messages_allocated ++; if (M1) { message_del_use (M1); + message_del_peer (M1); free_message (M1); memcpy (M1, M, sizeof (*M)); free (M); message_add_use (M1); + message_add_peer (M1); messages_allocated --; return M1; } else { message_add_use (M); + message_add_peer (M); message_tree = tree_insert_message (message_tree, M, lrand48 ()); return M; } @@ -1017,14 +1182,17 @@ struct message *fetch_alloc_geo_message (void) { messages_allocated ++; if (M1) { message_del_use (M1); + message_del_peer (M1); free_message (M1); memcpy (M1, M, sizeof (*M)); free (M); message_add_use (M1); + message_add_peer (M1); messages_allocated --; return M1; } else { message_add_use (M); + message_add_peer (M); message_tree = tree_insert_message (message_tree, M, lrand48 ()); return M; } @@ -1037,14 +1205,17 @@ struct message *fetch_alloc_encrypted_message (void) { messages_allocated ++; if (M1) { message_del_use (M1); + message_del_peer (M1); free_message (M1); memcpy (M1, M, sizeof (*M)); free (M); message_add_use (M1); + message_add_peer (M1); messages_allocated --; return M1; } else { message_add_use (M); + message_add_peer (M); message_tree = tree_insert_message (message_tree, M, lrand48 ()); return M; } @@ -1057,14 +1228,17 @@ struct message *fetch_alloc_message_short (void) { messages_allocated ++; if (M1) { message_del_use (M1); + message_del_peer (M1); free_message (M1); memcpy (M1, M, sizeof (*M)); free (M); message_add_use (M1); + message_add_peer (M1); messages_allocated --; return M1; } else { message_add_use (M); + message_add_peer (M); message_tree = tree_insert_message (message_tree, M, lrand48 ()); return M; } @@ -1080,14 +1254,17 @@ struct message *fetch_alloc_message_short_chat (void) { messages_allocated ++; if (M1) { message_del_use (M1); + message_del_peer (M1); free_message (M1); memcpy (M1, M, sizeof (*M)); free (M); message_add_use (M1); + message_add_peer (M1); messages_allocated --; return M1; } else { message_add_use (M); + message_add_peer (M); message_tree = tree_insert_message (message_tree, M, lrand48 ()); return M; } @@ -1106,7 +1283,7 @@ struct chat *fetch_alloc_chat (void) { memset (U, 0, sizeof (*U)); fetch_chat (&U->chat); peer_tree = tree_insert_peer (peer_tree, U, lrand48 ()); - Peers[(chat_num ++) + user_num] = U; + Peers[peer_num ++] = U; return &U->chat; } } @@ -1125,7 +1302,7 @@ struct chat *fetch_alloc_chat_full (void) { U->id = MK_CHAT (data[2]); peer_tree = tree_insert_peer (peer_tree, U, lrand48 ()); fetch_chat_full (&U->chat); - Peers[(chat_num ++) + user_num] = U; + Peers[peer_num ++] = U; return &U->chat; } } @@ -1139,9 +1316,13 @@ int print_stat (char *s, int len) { return snprintf (s, len, "users_allocated\t%d\n" "chats_allocated\t%d\n" + "secret_chats_allocated\t%d\n" + "peer_num\t%d\n" "messages_allocated\t%d\n", users_allocated, chats_allocated, + encr_chats_allocated, + peer_num, messages_allocated ); } @@ -1166,5 +1347,6 @@ void update_message_id (struct message *M, long long id) { void message_insert (struct message *M) { message_add_use (M); + message_add_peer (M); message_tree = tree_insert_message (message_tree, M, lrand48 ()); } diff --git a/structures.h b/structures.h index e703b69..5fdd8b7 100644 --- a/structures.h +++ b/structures.h @@ -20,7 +20,7 @@ #define __STRUCTURES_H__ #include -typedef struct { int id; } peer_id_t; +typedef struct { int type; int id; } peer_id_t; #define FLAG_EMPTY 1 #define FLAG_DELETED 2 @@ -111,6 +111,7 @@ struct user_status { struct user { peer_id_t id; int flags; + struct message *last; char *print_name; struct file_location photo_big; struct file_location photo_small; @@ -134,6 +135,7 @@ struct chat_user { struct chat { peer_id_t id; int flags; + struct message *last; char *print_title; struct file_location photo_big; struct file_location photo_small; @@ -157,6 +159,7 @@ enum secret_chat_state { struct secret_chat { peer_id_t id; int flags; + struct message *last; char *print_name; struct file_location photo_big; struct file_location photo_small; @@ -178,6 +181,7 @@ typedef union peer { struct { peer_id_t id; int flags; + struct message *last; char *print_name; struct file_location photo_big; struct file_location photo_small; @@ -237,6 +241,7 @@ struct message_media { struct message { struct message *next_use, *prev_use; + struct message *next, *prev; long long id; int flags; peer_id_t fwd_from_id; @@ -283,6 +288,7 @@ void update_message_id (struct message *M, long long id); void message_insert (struct message *M); void free_photo (struct photo *P); void fetch_photo (struct photo *P); +void insert_encrypted_chat (peer_t *P); #define PEER_USER 1 #define PEER_CHAT 2 @@ -296,55 +302,18 @@ void fetch_photo (struct photo *P); #define MK_ENCR_CHAT(id) set_peer_id (PEER_ENCR_CHAT,id) static inline int get_peer_type (peer_id_t id) { - if (id.id > 1000000000) { - return PEER_ENCR_CHAT; - } - if (id.id > 0) { - return PEER_USER; - } - if (id.id < -1000000000) { - return PEER_GEO_CHAT; - } - if (id.id < 0) { - return PEER_CHAT; - } - return PEER_UNKNOWN; + return id.type; } static inline int get_peer_id (peer_id_t id) { - switch (get_peer_type (id)) { - case PEER_USER: - return id.id; - case PEER_CHAT: - return -id.id; - case PEER_GEO_CHAT: - return -id.id - 1000000000; - case PEER_ENCR_CHAT: - return id.id - 1000000000; - default: - return 0; - } + return id.id; } static inline peer_id_t set_peer_id (int type, int id) { peer_id_t ID; - switch (type) { - case PEER_USER: - ID.id = id; - return ID; - case PEER_CHAT: - ID.id = -id; - return ID; - case PEER_GEO_CHAT: - ID.id = -id - 1000000000; - return ID; - case PEER_ENCR_CHAT: - ID.id = id + 1000000000; - return ID; - default: - assert (0); - return ID; - } + ID.id = id; + ID.type = type; + return ID; } static inline int cmp_peer_id (peer_id_t a, peer_id_t b) {