tg/queries.c

572 lines
14 KiB
C
Raw Normal View History

2013-10-11 16:52:20 -04:00
#include <string.h>
#include <memory.h>
#include <stdlib.h>
#include <zlib.h>
#include "include.h"
#include "mtproto-client.h"
#include "queries.h"
#include "tree.h"
#include "mtproto-common.h"
#include "telegram.h"
#include "loop.h"
#include "structures.h"
#include "interface.h"
int verbosity;
#define QUERY_TIMEOUT 0.3
#define memcmp8(a,b) memcmp ((a), (b), 8)
DEFINE_TREE (query, struct query *, memcmp8, 0) ;
struct tree_query *queries_tree;
double get_double_time (void) {
struct timespec tv;
clock_gettime (CLOCK_REALTIME, &tv);
return tv.tv_sec + 1e-9 * tv.tv_nsec;
}
struct query *query_get (long long id) {
return tree_lookup_query (queries_tree, (void *)&id);
}
int alarm_query (struct query *q) {
assert (q);
q->ev.timeout = get_double_time () + QUERY_TIMEOUT;
insert_event_timer (&q->ev);
2013-10-11 16:52:20 -04:00
return 0;
}
struct query *send_query (struct dc *DC, int ints, void *data, struct query_methods *methods) {
assert (DC);
assert (DC->auth_key_id);
if (!DC->sessions[0]) {
dc_create_session (DC);
}
if (verbosity) {
logprintf ( "Sending query of size %d to DC (%s:%d)\n", 4 * ints, DC->ip, DC->port);
2013-10-11 16:52:20 -04:00
}
struct query *q = malloc (sizeof (*q));
2013-10-16 15:19:39 -04:00
memset (q, 0, sizeof (*q));
2013-10-11 16:52:20 -04:00
q->data_len = ints;
q->data = malloc (4 * ints);
memcpy (q->data, data, 4 * ints);
q->msg_id = encrypt_send_message (DC->sessions[0]->c, data, ints, 1);
if (verbosity) {
logprintf ( "Msg_id is %lld\n", q->msg_id);
2013-10-11 16:52:20 -04:00
}
q->methods = methods;
if (queries_tree) {
logprintf ( "%lld %lld\n", q->msg_id, queries_tree->x->msg_id);
2013-10-11 16:52:20 -04:00
}
queries_tree = tree_insert_query (queries_tree, q, lrand48 ());
q->ev.alarm = (void *)alarm_query;
q->ev.timeout = get_double_time () + QUERY_TIMEOUT;
q->ev.self = (void *)q;
insert_event_timer (&q->ev);
return q;
}
void query_ack (long long id) {
struct query *q = query_get (id);
if (q && !(q->flags & QUERY_ACK_RECEIVED)) {
2013-10-16 15:19:39 -04:00
remove_event_timer (&q->ev);
q->flags |= QUERY_ACK_RECEIVED;
}
2013-10-11 16:52:20 -04:00
}
void query_error (long long id) {
assert (fetch_int () == CODE_rpc_error);
int error_code = fetch_int ();
int error_len = prefetch_strlen ();
char *error = fetch_str (error_len);
if (verbosity) {
logprintf ( "error for query #%lld: #%d :%.*s\n", id, error_code, error_len, error);
2013-10-11 16:52:20 -04:00
}
struct query *q = query_get (id);
if (!q) {
if (verbosity) {
logprintf ( "No such query\n");
2013-10-11 16:52:20 -04:00
}
} else {
2013-10-16 15:19:39 -04:00
if (!(q->flags & QUERY_ACK_RECEIVED)) {
remove_event_timer (&q->ev);
}
2013-10-11 16:52:20 -04:00
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);
}
free (q->data);
free (q);
}
}
#define MAX_PACKED_SIZE (1 << 20)
static int packed_buffer[MAX_PACKED_SIZE / 4];
void query_result (long long id UU) {
if (verbosity) {
logprintf ( "result for query #%lld\n", id);
2013-10-11 16:52:20 -04:00
}
if (verbosity >= 4) {
logprintf ( "result: ");
2013-10-11 16:52:20 -04:00
hexdump_in ();
}
int op = prefetch_int ();
int *end = 0;
int *eend = 0;
if (op == CODE_gzip_packed) {
fetch_int ();
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 *)packed_buffer;
int err = inflate (&strm, Z_FINISH);
if (verbosity) {
logprintf ( "inflate error = %d\n", err);
logprintf ( "inflated %d bytes\n", (int)strm.total_out);
2013-10-11 16:52:20 -04:00
}
end = in_ptr;
eend = in_end;
assert (dl % 4 == 0);
in_ptr = packed_buffer;
in_end = in_ptr + strm.total_out / 4;
if (verbosity >= 4) {
logprintf ( "Unzipped data: ");
2013-10-11 16:52:20 -04:00
hexdump_in ();
}
}
struct query *q = query_get (id);
if (!q) {
if (verbosity) {
logprintf ( "No such query\n");
2013-10-11 16:52:20 -04:00
}
} else {
2013-10-16 15:19:39 -04:00
if (!(q->flags & QUERY_ACK_RECEIVED)) {
remove_event_timer (&q->ev);
}
2013-10-11 16:52:20 -04:00
queries_tree = tree_delete_query (queries_tree, q);
if (q->methods && q->methods->on_answer) {
q->methods->on_answer (q);
}
free (q->data);
free (q);
}
if (end) {
in_ptr = end;
in_end = eend;
}
}
#define event_timer_cmp(a,b) ((a)->timeout > (b)->timeout ? 1 : ((a)->timeout < (b)->timeout ? -1 : (memcmp (a, b, sizeof (struct event_timer)))))
DEFINE_TREE (timer, struct event_timer *, event_timer_cmp, 0)
struct tree_timer *timer_tree;
void insert_event_timer (struct event_timer *ev) {
if (verbosity > 2) {
logprintf ( "INSERT: %lf %p %p\n", ev->timeout, ev->self, ev->alarm);
}
2013-10-11 16:52:20 -04:00
tree_check_timer (timer_tree);
timer_tree = tree_insert_timer (timer_tree, ev, lrand48 ());
tree_check_timer (timer_tree);
}
void remove_event_timer (struct event_timer *ev) {
if (verbosity > 2) {
logprintf ( "REMOVE: %lf %p %p\n", ev->timeout, ev->self, ev->alarm);
}
2013-10-11 16:52:20 -04:00
tree_check_timer (timer_tree);
timer_tree = tree_delete_timer (timer_tree, ev);
tree_check_timer (timer_tree);
}
double next_timer_in (void) {
if (!timer_tree) { return 1e100; }
return tree_get_min_timer (timer_tree)->timeout;
}
void work_timers (void) {
double t = get_double_time ();
while (timer_tree) {
struct event_timer *ev = tree_get_min_timer (timer_tree);
assert (ev);
if (ev->timeout > t) { break; }
remove_event_timer (ev);
2013-10-16 15:19:39 -04:00
assert (ev->alarm);
if (verbosity) {
logprintf ("Alarm\n");
}
2013-10-11 16:52:20 -04:00
ev->alarm (ev->self);
}
}
int max_chat_size;
int want_dc_num;
extern struct dc *DC_list[];
extern struct dc *DC_working;
int help_get_config_on_answer (struct query *q UU) {
assert (fetch_int () == CODE_config);
fetch_int ();
unsigned test_mode = fetch_int ();
assert (test_mode == CODE_bool_true || test_mode == CODE_bool_false);
assert (test_mode == CODE_bool_false);
int this_dc = fetch_int ();
if (verbosity) {
logprintf ( "this_dc = %d\n", this_dc);
2013-10-11 16:52:20 -04:00
}
assert (fetch_int () == CODE_vector);
int n = fetch_int ();
assert (n <= 10);
int i;
for (i = 0; i < n; i++) {
assert (fetch_int () == CODE_dc_option);
int id = fetch_int ();
int l1 = prefetch_strlen ();
char *name = fetch_str (l1);
int l2 = prefetch_strlen ();
char *ip = fetch_str (l2);
int port = fetch_int ();
if (verbosity) {
logprintf ( "id = %d, name = %.*s ip = %.*s port = %d\n", id, l1, name, l2, ip, port);
2013-10-11 16:52:20 -04:00
}
if (!DC_list[id]) {
alloc_dc (id, strndup (ip, l2), port);
}
}
max_chat_size = fetch_int ();
if (verbosity >= 2) {
logprintf ( "chat_size = %d\n", max_chat_size);
2013-10-11 16:52:20 -04:00
}
return 0;
}
struct query_methods help_get_config_methods = {
.on_answer = help_get_config_on_answer
};
char *phone_code_hash;
int send_code_on_answer (struct query *q UU) {
assert (fetch_int () == CODE_auth_sent_code);
assert (fetch_int () == (int)CODE_bool_true);
int l = prefetch_strlen ();
char *s = fetch_str (l);
if (phone_code_hash) {
free (phone_code_hash);
}
phone_code_hash = strndup (s, l);
want_dc_num = -1;
return 0;
}
int send_code_on_error (struct query *q UU, int error_code, int l, char *error) {
int s = strlen ("PHONE_MIGRATE_");
if (l >= s && !memcmp (error, "PHONE_MIGRATE_", s)) {
int i = error[s] - '0';
want_dc_num = i;
} else {
logprintf ( "error_code = %d, error = %.*s\n", error_code, l, error);
2013-10-11 16:52:20 -04:00
assert (0);
}
return 0;
}
struct query_methods send_code_methods = {
.on_answer = send_code_on_answer,
.on_error = send_code_on_error
};
int code_is_sent (void) {
return want_dc_num;
}
int config_got (void) {
return DC_list[want_dc_num] != 0;
}
char *suser;
extern int dc_working_num;
void do_send_code (const char *user) {
suser = strdup (user);
want_dc_num = 0;
clear_packet ();
out_int (CODE_auth_send_code);
out_string (user);
out_int (0);
out_int (TG_APP_ID);
out_string (TG_APP_HASH);
send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &send_code_methods);
net_loop (0, code_is_sent);
if (want_dc_num == -1) { return; }
if (DC_list[want_dc_num]) {
DC_working = DC_list[want_dc_num];
if (!DC_working->auth_key_id) {
dc_authorize (DC_working);
}
if (!DC_working->sessions[0]) {
dc_create_session (DC_working);
}
dc_working_num = want_dc_num;
} else {
clear_packet ();
out_int (CODE_help_get_config);
send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &help_get_config_methods);
net_loop (0, config_got);
DC_working = DC_list[want_dc_num];
if (!DC_working->auth_key_id) {
dc_authorize (DC_working);
}
if (!DC_working->sessions[0]) {
dc_create_session (DC_working);
}
dc_working_num = want_dc_num;
}
want_dc_num = 0;
clear_packet ();
out_int (CODE_auth_send_code);
out_string (user);
out_int (0);
out_int (TG_APP_ID);
out_string (TG_APP_HASH);
send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &send_code_methods);
net_loop (0, code_is_sent);
assert (want_dc_num == -1);
}
int sign_in_ok;
int sign_in_is_ok (void) {
return sign_in_ok;
}
struct user User;
int sign_in_on_answer (struct query *q UU) {
assert (fetch_int () == (int)CODE_auth_authorization);
int expires = fetch_int ();
fetch_user (&User);
sign_in_ok = 1;
if (verbosity) {
logprintf ( "authorized successfully: name = '%s %s', phone = '%s', expires = %d\n", User.first_name, User.last_name, User.phone, (int)(expires - get_double_time ()));
2013-10-11 16:52:20 -04:00
}
return 0;
}
int sign_in_on_error (struct query *q UU, int error_code, int l, char *error) {
logprintf ( "error_code = %d, error = %.*s\n", error_code, l, error);
2013-10-11 16:52:20 -04:00
sign_in_ok = -1;
return 0;
}
struct query_methods sign_in_methods = {
.on_answer = sign_in_on_answer,
.on_error = sign_in_on_error
};
int do_send_code_result (const char *code) {
clear_packet ();
out_int (CODE_auth_sign_in);
2013-10-11 16:52:20 -04:00
out_string (suser);
out_string (phone_code_hash);
out_string (code);
send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &sign_in_methods);
sign_in_ok = 0;
net_loop (0, sign_in_is_ok);
return sign_in_ok;
}
extern char *user_list[];
2013-10-11 16:52:20 -04:00
int get_contacts_on_answer (struct query *q UU) {
int i;
2013-10-11 16:52:20 -04:00
assert (fetch_int () == (int)CODE_contacts_contacts);
assert (fetch_int () == CODE_vector);
int n = fetch_int ();
for (i = 0; i < n; i++) {
assert (fetch_int () == (int)CODE_contact);
fetch_int (); // id
fetch_int (); // mutual
}
assert (fetch_int () == CODE_vector);
n = fetch_int ();
for (i = 0; i < n; i++) {
struct user *U = fetch_alloc_user ();
rprintf ("User #%d: " COLOR_RED "%s %s" COLOR_NORMAL " (" COLOR_GREEN "%s" COLOR_NORMAL ")\n", U->id, U->first_name, U->last_name, U->print_name);
2013-10-11 16:52:20 -04:00
}
return 0;
}
struct query_methods get_contacts_methods = {
.on_answer = get_contacts_on_answer,
};
void do_update_contact_list (void) {
clear_packet ();
out_int (CODE_contacts_get_contacts);
out_string ("");
send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &get_contacts_methods);
}
2013-10-16 15:19:39 -04:00
int msg_send_on_answer (struct query *q UU) {
assert (fetch_int () == (int)CODE_messages_sent_message);
int uid = fetch_int (); // uid
int date = fetch_int (); // date
int ptr = fetch_int (); // ptr
int seq = fetch_int (); // seq
logprintf ("Sent: uid = %d, date = %d, ptr = %d, seq = %d\n", uid, date, ptr, seq);
return 0;
}
struct query_methods msg_send_methods = {
.on_answer = msg_send_on_answer
};
int out_message_num;
void do_send_message (union user_chat *U, const char *msg) {
if (!out_message_num) {
out_message_num = lrand48 ();
}
clear_packet ();
out_int (CODE_messages_send_message);
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_string (msg);
out_long (out_message_num ++);
send_query (DC_working, packet_ptr - packet_buffer, packet_buffer, &msg_send_methods);
if (U->id < 0) {
rprintf (COLOR_RED "%s" COLOR_GREEN " <<< " COLOR_NORMAL "%s\n", U->chat.title, msg);
} else {
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);
}