/*
This file is part of telegram-client.
Telegram-client 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
(at your option) any later version.
Telegram-client 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this telegram-client. If not, see .
Copyright Vitaly Valtman 2013
*/
#include
#include "structures.h"
#include "mtproto-common.h"
#include "telegram.h"
#include "tree.h"
#include "loop.h"
int verbosity;
void fetch_file_location (struct file_location *loc) {
int x = fetch_int ();
if (x == CODE_file_location_unavailable) {
loc->dc = -1;
loc->volume = fetch_long ();
loc->local_id = fetch_int ();
loc->secret = fetch_long ();
} else {
assert (x == CODE_file_location);
loc->dc = fetch_int ();;
loc->volume = fetch_long ();
loc->local_id = fetch_int ();
loc->secret = fetch_long ();
}
}
void fetch_user_status (struct user_status *S) {
int x = fetch_int ();
switch (x) {
case CODE_user_status_empty:
S->online = 0;
break;
case CODE_user_status_online:
S->online = 1;
S->when = fetch_int ();
break;
case CODE_user_status_offline:
S->online = -1;
S->when = fetch_int ();
break;
default:
assert (0);
}
}
int our_id;
void fetch_user (struct user *U) {
unsigned x = fetch_int ();
assert (x == CODE_user_empty || x == CODE_user_self || x == CODE_user_contact || x == CODE_user_request || x == CODE_user_foreign || x == CODE_user_deleted);
U->id = fetch_int ();
U->flags &= ~(FLAG_EMPTY | FLAG_DELETED | FLAG_USER_SELF | FLAG_USER_FOREIGN | FLAG_USER_CONTACT);
if (x == CODE_user_empty) {
U->flags |= FLAG_EMPTY;
return;
}
if (x == CODE_user_self) {
assert (!our_id || (our_id == U->id));
if (!our_id) {
our_id = U->id;
write_auth_file ();
}
}
if (U->first_name) { free (U->first_name); }
if (U->last_name) { free (U->last_name); }
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++;
}
if (x == CODE_user_deleted) {
U->flags |= FLAG_DELETED;
return;
}
if (x == CODE_user_self) {
U->flags |= FLAG_USER_SELF;
} else {
U->access_hash = fetch_long ();
}
if (x == CODE_user_foreign) {
U->flags |= FLAG_USER_FOREIGN;
} else {
if (U->phone) { free (U->phone); }
U->phone = fetch_str_dup ();
}
unsigned y = fetch_int ();
if (y == CODE_user_profile_photo_empty) {
U->photo_small.dc = -2;
U->photo_big.dc = -2;
} else {
assert (y == CODE_user_profile_photo);
fetch_file_location (&U->photo_small);
fetch_file_location (&U->photo_big);
}
fetch_user_status (&U->status);
if (x == CODE_user_self) {
assert (fetch_int () == (int)CODE_bool_false);
}
if (x == CODE_user_contact) {
U->flags |= FLAG_USER_CONTACT;
}
}
void fetch_chat (struct chat *C) {
unsigned x = fetch_int ();
assert (x == CODE_chat_empty || x == CODE_chat || x == CODE_chat_forbidden);
C->id = -fetch_int ();
C->flags &= ~(FLAG_EMPTY | FLAG_DELETED | FLAG_FORBIDDEN | FLAG_CHAT_IN_CHAT);
if (x == CODE_chat_empty) {
C->flags |= FLAG_EMPTY;
return;
}
if (x == CODE_chat_forbidden) {
C->flags |= FLAG_FORBIDDEN;
}
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 ++;
}
if (x == CODE_chat) {
unsigned y = fetch_int ();
if (y == CODE_chat_photo_empty) {
C->photo_small.dc = -2;
C->photo_big.dc = -2;
} else {
assert (y == CODE_chat_photo);
fetch_file_location (&C->photo_small);
fetch_file_location (&C->photo_big);
}
C->users_num = fetch_int ();
C->date = fetch_int ();
if (fetch_int () == (int)CODE_bool_true) {
C->flags |= FLAG_CHAT_IN_CHAT;
}
C->version = fetch_int ();
} else {
C->photo_small.dc = -2;
C->photo_big.dc = -2;
C->users_num = -1;
C->date = fetch_int ();
C->version = -1;
}
}
void fetch_notify_settings (void) {
unsigned x = fetch_int ();
assert (x == CODE_peer_notify_settings || x == CODE_peer_notify_settings_empty);
if (x == CODE_peer_notify_settings) {
fetch_int (); // mute_until
int l = prefetch_strlen ();
fetch_str (l);
fetch_bool (); // show_previews
fetch_int (); // peer notify events
}
}
void fetch_chat_full (struct chat *C) {
unsigned x = fetch_int ();
assert (x == CODE_messages_chat_full);
assert (fetch_int () == CODE_chat_full);
C->id = -fetch_int ();
C->flags &= ~(FLAG_EMPTY | FLAG_DELETED | FLAG_FORBIDDEN | FLAG_CHAT_IN_CHAT);
x = fetch_int ();
if (x == CODE_chat_participants) {
assert (fetch_int () == -C->id);
C->admin_id = fetch_int ();
assert (fetch_int () == CODE_vector);
if (C->users) {
free (C->users);
}
C->users_num = fetch_int ();
C->users = malloc (sizeof (struct chat_user) * C->users_num);
int i;
for (i = 0; i < C->users_num; i++) {
assert (fetch_int () == (int)CODE_chat_participant);
C->users[i].user_id = fetch_int ();
C->users[i].inviter_id = fetch_int ();
C->users[i].date = fetch_int ();
}
C->version = fetch_int ();
} else {
C->flags |= FLAG_FORBIDDEN;
assert (x == CODE_chat_participants_forbidden);
}
if (C->flags & FLAG_HAS_PHOTO) {
free_photo (&C->photo);
}
fetch_photo (&C->photo);
C->flags |= FLAG_HAS_PHOTO;
fetch_notify_settings ();
int n, 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 ();
}
}
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 || x == CODE_photo_size_empty);
S->type = fetch_str_dup ();
if (x != CODE_photo_size_empty) {
fetch_file_location (&S->loc);
S->w = fetch_int ();
S->h = fetch_int ();
if (x == CODE_photo_size) {
S->size = fetch_int ();
} else {
S->data = fetch_str_dup ();
}
}
}
void fetch_geo (struct geo *G) {
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) {
memset (P, 0, sizeof (*P));
unsigned x = fetch_int ();
assert (x == CODE_photo_empty || x == CODE_photo);
P->id = fetch_long ();
if (x == CODE_photo_empty) { return; }
P->access_hash = fetch_long ();
P->user_id = fetch_int ();
P->date = fetch_int ();
P->caption = fetch_str_dup ();
fetch_geo (&P->geo);
assert (fetch_int () == CODE_vector);
P->sizes_num = fetch_int ();
P->sizes = malloc (sizeof (struct photo_size) * P->sizes_num);
int i;
for (i = 0; i < P->sizes_num; i++) {
fetch_photo_size (&P->sizes[i]);
}
}
void fetch_video (struct video *V) {
memset (V, 0, sizeof (*V));
unsigned x = fetch_int ();
V->id = fetch_long ();
if (x == CODE_video_empty) { return; }
V->access_hash = fetch_long ();
V->user_id = fetch_int ();
V->date = fetch_int ();
V->caption = fetch_str_dup ();
V->duration = fetch_int ();
V->size = fetch_int ();
fetch_photo_size (&V->thumb);
V->dc_id = fetch_int ();
V->w = fetch_int ();
V->h = fetch_int ();
}
void fetch_message_action (struct message_action *M) {
memset (M, 0, sizeof (*M));
unsigned x = fetch_int ();
M->type = x;
switch (x) {
case CODE_message_action_empty:
break;
case CODE_message_action_chat_create:
M->title = fetch_str_dup ();
assert (fetch_int () == (int)CODE_vector);
M->user_num = fetch_int ();
M->users = malloc (M->user_num * 4);
fetch_ints (M->users, M->user_num);
break;
case CODE_message_action_chat_edit_title:
M->new_title = fetch_str_dup ();
break;
case CODE_message_action_chat_edit_photo:
fetch_photo (&M->photo);
break;
case CODE_message_action_chat_delete_photo:
break;
case CODE_message_action_chat_add_user:
M->user = fetch_int ();
break;
case CODE_message_action_chat_delete_user:
M->user = fetch_int ();
break;
default:
assert (0);
}
}
void fetch_message_short (struct message *M) {
memset (M, 0, sizeof (*M));
M->id = fetch_int ();
M->from_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;
M->unread = 1;
}
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->message = fetch_str_dup ();
fetch_int (); // pts
M->date = fetch_int ();
fetch_int (); // seq
M->media.type = CODE_message_media_empty;
M->unread = 1;
}
void fetch_message_media (struct message_media *M) {
memset (M, 0, sizeof (*M));
M->type = fetch_int ();
switch (M->type) {
case CODE_message_media_empty:
break;
case CODE_message_media_photo:
fetch_photo (&M->photo);
break;
case CODE_message_media_video:
fetch_video (&M->video);
break;
case CODE_message_media_geo:
fetch_geo (&M->geo);
break;
case CODE_message_media_contact:
M->phone = fetch_str_dup ();
M->first_name = fetch_str_dup ();
M->last_name = fetch_str_dup ();
M->user_id = fetch_int ();
break;
case CODE_message_media_unsupported:
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 ();
assert (x == CODE_message_empty || x == CODE_message || x == CODE_message_forwarded || x == CODE_message_service);
M->id = fetch_int ();
if (x == CODE_message_empty) {
M->flags |= 1;
return;
}
if (x == CODE_message_forwarded) {
M->fwd_from_id = fetch_int ();
M->fwd_date = fetch_int ();
}
M->from_id = fetch_int ();
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;
fetch_message_action (&M->action);
} else {
M->message = fetch_str_dup ();
fetch_message_media (&M->media);
}
}
#define user_cmp(a,b) ((a)->id - (b)->id)
DEFINE_TREE(peer,union user_chat *,user_cmp,0)
DEFINE_TREE(message,struct message *,user_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;
union user_chat *Peers[MAX_USER_NUM];
struct message message_list = {
.next_use = &message_list,
.prev_use = &message_list
};
struct user *fetch_alloc_user (void) {
int data[2];
prefetch_data (data, 8);
union user_chat *U = user_chat_get (data[1]);
if (U) {
fetch_user (&U->user);
return &U->user;
} else {
users_allocated ++;
U = malloc (sizeof (*U));
memset (U, 0, sizeof (*U));
fetch_user (&U->user);
peer_tree = tree_insert_peer (peer_tree, U, lrand48 ());
Peers[chat_num + (user_num ++)] = U;
return &U->user;
}
}
void free_user (struct user *U) {
if (U->first_name) { free (U->first_name); }
if (U->last_name) { free (U->last_name); }
if (U->print_name) { free (U->print_name); }
if (U->phone) { free (U->phone); }
}
void free_photo_size (struct photo_size *S) {
free (S->type);
if (S->data) {
free (S->data);
}
}
void free_photo (struct photo *P) {
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);
}
}
void free_video (struct video *V) {
if (!V->access_hash) { return; }
free (V->caption);
free_photo_size (&V->thumb);
}
void free_message_media (struct message_media *M) {
switch (M->type) {
case CODE_message_media_empty:
case CODE_message_media_geo:
return;
case CODE_message_media_photo:
free_photo (&M->photo);
return;
case CODE_message_media_video:
free_video (&M->video);
return;
case CODE_message_media_contact:
free (M->phone);
free (M->first_name);
free (M->last_name);
return;
case CODE_message_media_unsupported:
free (M->data);
return;
default:
logprintf ("%08x\n", M->type);
assert (0);
}
}
void free_message_action (struct message_action *M) {
switch (M->type) {
case CODE_message_action_empty:
break;
case CODE_message_action_chat_create:
free (M->title);
free (M->users);
break;
case CODE_message_action_chat_edit_title:
free (M->new_title);
break;
case CODE_message_action_chat_edit_photo:
free_photo (&M->photo);
break;
case CODE_message_action_chat_delete_photo:
break;
case CODE_message_action_chat_add_user:
break;
case CODE_message_action_chat_delete_user:
break;
default:
assert (0);
}
}
void free_message (struct message *M) {
if (!M->service) {
if (M->message) { free (M->message); }
free_message_media (&M->media);
} else {
free_message_action (&M->action);
}
}
void message_del_use (struct message *M) {
M->next_use->prev_use = M->prev_use;
M->prev_use->next_use = M->next_use;
}
void message_add_use (struct message *M) {
M->next_use = message_list.next_use;
M->prev_use = &message_list;
M->next_use->prev_use = M;
M->prev_use->next_use = M;
}
struct message *fetch_alloc_message (void) {
struct message *M = malloc (sizeof (*M));
fetch_message (M);
struct message *M1 = tree_lookup_message (message_tree, M);
messages_allocated ++;
if (M1) {
message_del_use (M1);
free_message (M1);
memcpy (M1, M, sizeof (*M));
free (M);
message_add_use (M1);
messages_allocated --;
return M1;
} else {
message_add_use (M);
message_tree = tree_insert_message (message_tree, M, lrand48 ());
return M;
}
}
struct message *fetch_alloc_message_short (void) {
struct message *M = malloc (sizeof (*M));
fetch_message_short (M);
struct message *M1 = tree_lookup_message (message_tree, M);
messages_allocated ++;
if (M1) {
message_del_use (M1);
free_message (M1);
memcpy (M1, M, sizeof (*M));
free (M);
message_add_use (M1);
messages_allocated --;
return M1;
} else {
message_add_use (M);
message_tree = tree_insert_message (message_tree, M, lrand48 ());
return M;
}
}
struct message *fetch_alloc_message_short_chat (void) {
struct message *M = malloc (sizeof (*M));
fetch_message_short_chat (M);
if (verbosity >= 2) {
logprintf ("Read message with id %d\n", M->id);
}
struct message *M1 = tree_lookup_message (message_tree, M);
messages_allocated ++;
if (M1) {
message_del_use (M1);
free_message (M1);
memcpy (M1, M, sizeof (*M));
free (M);
message_add_use (M1);
messages_allocated --;
return M1;
} else {
message_add_use (M);
message_tree = tree_insert_message (message_tree, M, lrand48 ());
return M;
}
}
struct chat *fetch_alloc_chat (void) {
int data[2];
prefetch_data (data, 8);
union user_chat *U = user_chat_get (-data[1]);
if (U) {
fetch_chat (&U->chat);
return &U->chat;
} else {
chats_allocated ++;
U = malloc (sizeof (*U));
memset (U, 0, sizeof (*U));
fetch_chat (&U->chat);
peer_tree = tree_insert_peer (peer_tree, U, lrand48 ());
Peers[(chat_num ++) + user_num] = U;
return &U->chat;
}
}
struct chat *fetch_alloc_chat_full (void) {
int data[3];
prefetch_data (data, 12);
union user_chat *U = user_chat_get (-data[2]);
if (U) {
fetch_chat_full (&U->chat);
return &U->chat;
} else {
chats_allocated ++;
U = malloc (sizeof (*U));
memset (U, 0, sizeof (*U));
U->id = -data[2];
peer_tree = tree_insert_peer (peer_tree, U, lrand48 ());
fetch_chat_full (&U->chat);
Peers[(chat_num ++) + user_num] = U;
return &U->chat;
}
}
void free_chat (struct chat *U) {
if (U->title) { free (U->title); }
if (U->print_title) { free (U->print_title); }
}
int print_stat (char *s, int len) {
return snprintf (s, len,
"users_allocated\t%d\n"
"chats_allocated\t%d\n"
"messages_allocated\t%d\n",
users_allocated,
chats_allocated,
messages_allocated
);
}
union user_chat *user_chat_get (int id) {
union user_chat U;
U.id = id;
return tree_lookup_peer (peer_tree, &U);
}
struct message *message_get (int id) {
struct message M;
M.id = id;
return tree_lookup_message (message_tree, &M);
}
void update_message_id (struct message *M, int id) {
message_tree = tree_delete_message (message_tree, M);
M->id = id;
message_tree = tree_insert_message (message_tree, M, lrand48 ());
}
void message_insert (struct message *M) {
message_add_use (M);
message_tree = tree_insert_message (message_tree, M, lrand48 ());
}