diff --git a/interface.c b/interface.c index bedc835..af608ec 100644 --- a/interface.c +++ b/interface.c @@ -13,6 +13,8 @@ #include "interface.h" #include "telegram.h" #include "structures.h" + +#include "mtproto-common.h" char *default_prompt = ">"; char *get_default_prompt (void) { @@ -28,6 +30,8 @@ char *commands[] = { "msg", "contact_list", "stats", + "history", + "dialog_list", 0 }; int commands_flags[] = { @@ -35,6 +39,8 @@ int commands_flags[] = { 072, 07, 07, + 072, + 07, }; char *a = 0; @@ -194,6 +200,8 @@ void interpreter (char *line UU) { } if (!memcmp (line, "contact_list", 12)) { do_update_contact_list (); + } else if (!memcmp (line, "dialog_list", 11)) { + do_get_dialog_list (); } else if (!memcmp (line, "stats", 5)) { static char stat_buf[1 << 15]; print_stat (stat_buf, (1 << 15) - 1); @@ -203,13 +211,32 @@ void interpreter (char *line UU) { int len; char *text = get_token (&q, &len); int index = 0; - while (index < user_num + chat_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len) || Peers[index]->id < 0)) { + while (index < user_num + chat_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len))) { index ++; } while (*q && (*q == ' ' || *q == '\t')) { q ++; } if (*q && index < user_num + chat_num) { do_send_message (Peers[index], q); } + } else if (!memcmp (line, "history", 7)) { + char *q = line + 7; + int len; + char *text = get_token (&q, &len); + int index = 0; + while (index < user_num + chat_num && (!Peers[index]->print_name || strncmp (Peers[index]->print_name, text, len) || Peers[index]->id < 0)) { + index ++; + } + if (index < user_num + chat_num) { + char *text = get_token (&q, &len); + int limit = 40; + if (text) { + limit = atoi (text); + if (limit <= 0 || limit >= 1000000) { + limit = 40; + } + } + do_get_history (Peers[index], limit); + } } } @@ -289,13 +316,58 @@ 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] %.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 ""; + + } +} + void print_message (struct message *M) { - union user_chat *U = user_chat_get (M->from_id); - if (!M->service) { - if (U && U->id > 0) { - rprintf (COLOR_RED "%s %s " COLOR_GREEN " >>> " COLOR_NORMAL " %s\n", U->user.first_name, U->user.last_name, M->message); + if (M->service) { + rprintf ("Service message\n"); + return; + } + if (M->to_id >= 0) { + if (M->out) { + union user_chat *U = user_chat_get (M->to_id); + assert (M->from_id >= 0); + if (U) { + rprintf (COLOR_RED "%s %s" COLOR_GREEN " <<< %s %s" COLOR_NORMAL "\n", U->user.first_name, U->user.last_name, M->message, message_media_type_str (&M->media)); + } else { + rprintf (COLOR_RED "User #%d" COLOR_GREEN " <<< %s %s" COLOR_NORMAL "\n", M->from_id, M->message, message_media_type_str (&M->media)); + } } else { - rprintf (COLOR_RED "User #%d " COLOR_GREEN " >>> " COLOR_NORMAL " %s\n", M->from_id, M->message); + union user_chat *U = user_chat_get (M->from_id); + assert (M->from_id >= 0); + if (U) { + rprintf (COLOR_RED "%s %s" COLOR_BLUE " >>> %s %s" COLOR_NORMAL "\n", U->user.first_name, U->user.last_name, M->message, message_media_type_str (&M->media)); + } else { + rprintf (COLOR_RED "User #%d" COLOR_BLUE " >>> %s %s" COLOR_NORMAL "\n", M->from_id, M->message, message_media_type_str (&M->media)); + } + } + } else { + rprintf ("Message to chat %d\n", -M->to_id); + union user_chat *C = user_chat_get (M->to_id); + if (C) { + rprintf ("Chat %s\n", C->chat.title); } } } diff --git a/interface.h b/interface.h index 29e9e30..df1b1d3 100644 --- a/interface.h +++ b/interface.h @@ -6,6 +6,7 @@ #define COLOR_GREEN "\033[32;1m" #define COLOR_GREY "\033[37;1m" #define COLOR_YELLOW "\033[33;1m" +#define COLOR_BLUE "\033[34;1m" char *get_default_prompt (void); diff --git a/mtproto-client.c b/mtproto-client.c index 6ebcf16..168b075 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "net.h" #include "include.h" @@ -619,6 +620,7 @@ void work_update (struct connection *c UU, long long msg_id UU) { case CODE_update_new_message: { struct message *M = fetch_alloc_message (); + fetch_int (); //pts print_message (M); break; }; @@ -846,6 +848,45 @@ void work_rpc_result (struct connection *c UU, long long msg_id UU) { } } +#define MAX_PACKED_SIZE (1 << 20) +void work_packed (struct connection *c, long long msg_id) { + assert (fetch_int () == CODE_gzip_packed); + static int in_gzip; + static int buf[MAX_PACKED_SIZE >> 2]; + assert (!in_gzip); + in_gzip = 1; + + int l = prefetch_strlen (); + char *s = fetch_str (l); + size_t dl = MAX_PACKED_SIZE; + + z_stream strm = {0}; + assert (inflateInit2 (&strm, 16 + MAX_WBITS) == Z_OK); + strm.avail_in = l; + strm.next_in = (void *)s; + strm.avail_out = MAX_PACKED_SIZE; + strm.next_out = (void *)buf; + + int err = inflate (&strm, Z_FINISH); + if (verbosity) { + logprintf ( "inflate error = %d\n", err); + logprintf ( "inflated %d bytes\n", (int)strm.total_out); + } + int *end = in_ptr; + int *eend = in_end; + assert (dl % 4 == 0); + in_ptr = buf; + in_end = in_ptr + strm.total_out / 4; + if (verbosity >= 4) { + logprintf ( "Unzipped data: "); + hexdump_in (); + } + rpc_execute_answer (c, msg_id); + in_ptr = end; + in_end = eend; + in_gzip = 0; +} + void rpc_execute_answer (struct connection *c, long long msg_id UU) { if (verbosity >= 5) { hexdump_in (); @@ -876,6 +917,9 @@ void rpc_execute_answer (struct connection *c, long long msg_id UU) { case CODE_update_short_chat_message: work_update_short_chat_message (c, msg_id); return; + case CODE_gzip_packed: + work_packed (c, msg_id); + return; } logprintf ( "Unknown message: \n"); hexdump_in (); diff --git a/mtproto-common.h b/mtproto-common.h index be30560..498de9e 100644 --- a/mtproto-common.h +++ b/mtproto-common.h @@ -299,6 +299,14 @@ static inline int fetch_int (void) { return *(in_ptr ++); } +static inline int fetch_bool (void) { + if (verbosity > 6) { + logprintf ("fetch_bool: 0x%08x (%d)\n", *in_ptr, *in_ptr); + } + assert (*(in_ptr) == (int)CODE_bool_true || *(in_ptr) == (int)CODE_bool_false); + return *(in_ptr ++) == (int)CODE_bool_true; +} + static inline int prefetch_int (void) { return *(in_ptr); } diff --git a/queries.c b/queries.c index b984e74..807f111 100644 --- a/queries.c +++ b/queries.c @@ -33,6 +33,8 @@ struct query *query_get (long long id) { int alarm_query (struct query *q) { assert (q); + q->ev.timeout = get_double_time () + QUERY_TIMEOUT; + insert_event_timer (&q->ev); return 0; } @@ -69,7 +71,7 @@ struct query *send_query (struct dc *DC, int ints, void *data, struct query_meth void query_ack (long long id) { struct query *q = query_get (id); - if (q) { + if (q && !(q->flags & QUERY_ACK_RECEIVED)) { remove_event_timer (&q->ev); q->flags |= QUERY_ACK_RECEIVED; } @@ -462,3 +464,108 @@ void do_send_message (union user_chat *U, const char *msg) { rprintf (COLOR_RED "%s %s" COLOR_GREEN " <<< " COLOR_NORMAL "%s\n", U->user.first_name, U->user.last_name, msg); } } + +int get_history_on_answer (struct query *q UU) { + static struct message *ML[10000]; + int i; + int x = fetch_int (); + if (x == (int)CODE_messages_messages_slice) { + fetch_int (); + rprintf ("...\n"); + } else { + assert (x == (int)CODE_messages_messages); + } + assert (fetch_int () == CODE_vector); + int n = fetch_int (); + for (i = 0; i < n; i++) { + struct message *M = fetch_alloc_message (); + if (i <= 9999) { + ML[i] = M; + } + } + if (n > 10000) { n = 10000; } + for (i = n - 1; i >= 0; i--) { + print_message (ML[i]); + } + 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 (); + } + return 0; +} + +struct query_methods get_history_methods = { + .on_answer = get_history_on_answer, +}; + + +void do_get_history (union user_chat *U, int limit) { + clear_packet (); + out_int (CODE_messages_get_history); + if (U->id < 0) { + out_int (CODE_input_peer_chat); + out_int (-U->id); + } else { + if (U->user.access_hash) { + out_int (CODE_input_peer_foreign); + out_int (U->id); + out_long (U->user.access_hash); + } else { + out_int (CODE_input_peer_contact); + out_int (U->id); + } + } + out_int (0); + out_int (0); + out_int (limit); + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_history_methods); +} + +int get_dialogs_on_answer (struct query *q UU) { + assert (fetch_int () == CODE_messages_dialogs); + assert (fetch_int () == CODE_vector); + int n, i; + n = fetch_int (); + for (i = 0; i < n; i++) { + assert (fetch_int () == CODE_dialog); + fetch_peer_id (); + fetch_int (); + fetch_int (); + } + assert (fetch_int () == CODE_vector); + n = fetch_int (); + for (i = 0; i < n; i++) { + fetch_alloc_message (); + } + 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 (); + } + return 0; +} + +struct query_methods get_dialogs_methods = { + .on_answer = get_dialogs_on_answer, +}; + + +void do_get_dialog_list (void) { + clear_packet (); + out_int (CODE_messages_get_dialogs); + out_int (0); + out_int (0); + out_int (1000); + send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_dialogs_methods); +} diff --git a/queries.h b/queries.h index c13ed76..67a5698 100644 --- a/queries.h +++ b/queries.h @@ -46,4 +46,6 @@ double get_double_time (void); void do_update_contact_list (void); union user_chat; void do_send_message (union user_chat *U, const char *msg); +void do_get_history (union user_chat *U, int limit); +void do_get_dialog_list (void); #endif diff --git a/structures.c b/structures.c index bebe2b0..cf4a678 100644 --- a/structures.c +++ b/structures.c @@ -148,6 +148,7 @@ void fetch_chat (struct chat *C) { } void fetch_photo_size (struct photo_size *S) { + memset (S, 0, sizeof (*S)); unsigned x = fetch_int (); assert (x == CODE_photo_size || x == CODE_photo_cached_size); S->type = fetch_str_dup (); @@ -162,9 +163,15 @@ void fetch_photo_size (struct photo_size *S) { } void fetch_geo (struct geo *G) { - assert (fetch_int () == CODE_geo_point); - G->longitude = fetch_double (); - G->latitude = fetch_double (); + unsigned x = fetch_int (); + if (x == CODE_geo_point) { + G->longitude = fetch_double (); + G->latitude = fetch_double (); + } else { + assert (x == CODE_geo_point_empty); + G->longitude = 0; + G->latitude = 0; + } } void fetch_photo (struct photo *P) { @@ -244,17 +251,19 @@ void fetch_message_short (struct message *M) { fetch_int (); // pts M->date = fetch_int (); fetch_int (); // seq + M->media.type = CODE_message_media_empty; } void fetch_message_short_chat (struct message *M) { memset (M, 0, sizeof (*M)); M->id = fetch_int (); M->from_id = fetch_int (); - M->to_id = fetch_int (); + M->to_id = -fetch_int (); M->message = fetch_str_dup (); fetch_int (); // pts M->date = fetch_int (); fetch_int (); // seq + M->media.type = CODE_message_media_empty; } @@ -283,10 +292,21 @@ void fetch_message_media (struct message_media *M) { M->data = fetch_str_dup (); break; default: + logprintf ("type = 0x%08x\n", M->type); assert (0); } } +int fetch_peer_id (void) { + unsigned x =fetch_int (); + if (x == CODE_peer_user) { + return fetch_int (); + } else { + assert (CODE_peer_chat); + return -fetch_int (); + } +} + void fetch_message (struct message *M) { memset (M, 0, sizeof (*M)); unsigned x = fetch_int (); @@ -301,9 +321,9 @@ void fetch_message (struct message *M) { M->fwd_date = fetch_int (); } M->from_id = fetch_int (); - M->to_id = fetch_int (); - M->out = (fetch_int () == (int)CODE_bool_true); - M->unread = (fetch_int () == (int)CODE_bool_true); + M->to_id = fetch_peer_id (); + M->out = fetch_bool (); + M->unread = fetch_bool (); M->date = fetch_int (); if (x == CODE_message_service) { M->service = 1; @@ -360,21 +380,27 @@ void free_user (struct user *U) { void free_photo_size (struct photo_size *S) { free (S->type); - free (S->data); + if (S->data) { + free (S->data); + } } void free_photo (struct photo *P) { - free (P->caption); - int i; - for (i = 0; i < P->sizes_num; i++) { - free_photo_size (&P->sizes[i]); + if (!P->access_hash) { return; } + if (P->caption) { free (P->caption); } + if (P->sizes) { + int i; + for (i = 0; i < P->sizes_num; i++) { + free_photo_size (&P->sizes[i]); + } + free (P->sizes); } - free (P->sizes); } void free_video (struct video *V) { + if (!V->access_hash) { return; } free (V->caption); - free (&V->thumb); + free_photo_size (&V->thumb); } void free_message_media (struct message_media *M) { diff --git a/structures.h b/structures.h index 2b527c5..990fed6 100644 --- a/structures.h +++ b/structures.h @@ -149,6 +149,7 @@ struct chat *fetch_alloc_chat (void); struct message *fetch_alloc_message (void); struct message *fetch_alloc_message_short (void); struct message *fetch_alloc_message_short_chat (void); +int fetch_peer_id (void); void free_user (struct user *U); void free_chat (struct chat *U);